| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| node-npmtest-pomelo/ | 100% | (153 / 153) | 100% | (126 / 126) | 100% | (28 / 28) | 100% | (153 / 153) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/ | 92.31% | (12 / 13) | 75% | (3 / 4) | 50% | (1 / 2) | 92.31% | (12 / 13) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/ | 14.56% | (30 / 206) | 0% | (0 / 114) | 0% | (0 / 25) | 14.56% | (30 / 206) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/client/ | 12.05% | (10 / 83) | 0% | (0 / 36) | 0% | (0 / 13) | 12.05% | (10 / 83) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/master/ | 12.62% | (52 / 412) | 0% | (0 / 158) | 0% | (0 / 52) | 12.62% | (52 / 412) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/modules/ | 15.69% | (99 / 631) | 0.38% | (1 / 264) | 0% | (0 / 90) | 15.69% | (99 / 631) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/monitor/ | 19.81% | (21 / 106) | 0% | (0 / 34) | 0% | (0 / 19) | 19.81% | (21 / 106) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/protocol/mqtt/ | 16.58% | (31 / 187) | 0% | (0 / 42) | 0% | (0 / 32) | 16.58% | (31 / 187) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/lib/util/ | 19.03% | (47 / 247) | 0% | (0 / 102) | 0% | (0 / 34) | 19.03% | (47 / 247) | |
| node-npmtest-pomelo/node_modules/pomelo-admin/node_modules/pomelo-scheduler/lib/ | 20.64% | (77 / 373) | 0.62% | (1 / 162) | 8.11% | (3 / 37) | 20.7% | (77 / 372) | |
| node-npmtest-pomelo/node_modules/pomelo-loader/ | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| node-npmtest-pomelo/node_modules/pomelo-loader/lib/ | 17.54% | (10 / 57) | 0% | (0 / 26) | 0% | (0 / 8) | 17.54% | (10 / 57) | |
| node-npmtest-pomelo/node_modules/pomelo-logger/ | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| node-npmtest-pomelo/node_modules/pomelo-logger/lib/ | 35.92% | (51 / 142) | 11.76% | (10 / 85) | 31.58% | (6 / 19) | 35.92% | (51 / 142) | |
| node-npmtest-pomelo/node_modules/pomelo-logger/node_modules/log4js/lib/ | 49.02% | (225 / 459) | 22.71% | (52 / 229) | 38.46% | (40 / 104) | 50.79% | (225 / 443) | |
| node-npmtest-pomelo/node_modules/pomelo-logger/node_modules/log4js/lib/appenders/ | 91.67% | (11 / 12) | 75% | (3 / 4) | 100% | (3 / 3) | 91.67% | (11 / 12) | |
| node-npmtest-pomelo/node_modules/pomelo-monitor/ | 100% | (2 / 2) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (2 / 2) | |
| node-npmtest-pomelo/node_modules/pomelo-monitor/lib/ | 26.53% | (26 / 98) | 0% | (0 / 22) | 0% | (0 / 11) | 27.96% | (26 / 93) | |
| node-npmtest-pomelo/node_modules/pomelo-monitor/utils/ | 25% | (2 / 8) | 100% | (0 / 0) | 0% | (0 / 1) | 25% | (2 / 8) | |
| node-npmtest-pomelo/node_modules/pomelo-protobuf/lib/ | 18.09% | (53 / 293) | 0% | (0 / 139) | 0% | (0 / 36) | 18.15% | (53 / 292) | |
| node-npmtest-pomelo/node_modules/pomelo-protocol/ | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| node-npmtest-pomelo/node_modules/pomelo-protocol/lib/ | 20.21% | (39 / 193) | 3.45% | (3 / 87) | 6.67% | (1 / 15) | 20.21% | (39 / 193) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/ | 60% | (3 / 5) | 50% | (1 / 2) | 100% | (0 / 0) | 60% | (3 / 5) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/lib/rpc-client/ | 15.65% | (90 / 575) | 0% | (0 / 304) | 0% | (0 / 72) | 15.65% | (90 / 575) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/lib/rpc-client/mailboxes/ | 17.61% | (31 / 176) | 0% | (0 / 64) | 0% | (0 / 28) | 17.61% | (31 / 176) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/lib/rpc-server/ | 27.19% | (31 / 114) | 0% | (0 / 48) | 0% | (0 / 16) | 27.19% | (31 / 114) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/lib/rpc-server/acceptors/ | 17.07% | (21 / 123) | 0% | (0 / 34) | 0% | (0 / 21) | 17.07% | (21 / 123) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/lib/util/ | 22.08% | (51 / 231) | 0.96% | (1 / 104) | 3.03% | (1 / 33) | 22.08% | (51 / 231) | |
| node-npmtest-pomelo/node_modules/pomelo-rpc/node_modules/async/lib/ | 20.67% | (142 / 687) | 5.7% | (18 / 316) | 4.41% | (9 / 204) | 20.97% | (142 / 677) | |
| node-npmtest-pomelo/node_modules/pomelo-scheduler/lib/ | 20.64% | (77 / 373) | 0.62% | (1 / 162) | 8.11% | (3 / 37) | 20.7% | (77 / 372) | |
| node-npmtest-pomelo/node_modules/pomelo-scheduler/node_modules/log4js/lib/ | 38.44% | (153 / 398) | 14.63% | (30 / 205) | 22.99% | (20 / 87) | 39.23% | (153 / 390) | |
| node-npmtest-pomelo/node_modules/pomelo-scheduler/node_modules/log4js/lib/appenders/ | 83.33% | (10 / 12) | 75% | (3 / 4) | 66.67% | (2 / 3) | 83.33% | (10 / 12) | |
| node-npmtest-pomelo/node_modules/pomelo/ | 25% | (2 / 8) | 100% | (0 / 0) | 0% | (0 / 1) | 25% | (2 / 8) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/ | 34.35% | (147 / 428) | 9.3% | (16 / 172) | 15.28% | (11 / 72) | 34.35% | (147 / 428) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/common/manager/ | 16.67% | (13 / 78) | 0% | (0 / 28) | 0% | (0 / 14) | 16.67% | (13 / 78) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/common/remote/frontend/ | 23.08% | (6 / 26) | 0% | (0 / 4) | 0% | (0 / 5) | 23.08% | (6 / 26) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/common/service/ | 18.12% | (137 / 756) | 0% | (0 / 253) | 0% | (0 / 141) | 18.15% | (137 / 755) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/components/ | 20.32% | (141 / 694) | 0% | (0 / 282) | 0% | (0 / 113) | 20.38% | (141 / 692) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/connectors/ | 30.63% | (136 / 444) | 0% | (0 / 100) | 0% | (0 / 66) | 30.63% | (136 / 444) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/connectors/commands/ | 17.39% | (20 / 115) | 0% | (0 / 61) | 0% | (0 / 16) | 17.39% | (20 / 115) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/connectors/common/ | 30.34% | (27 / 89) | 0% | (0 / 49) | 0% | (0 / 10) | 30.34% | (27 / 89) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/connectors/hybrid/ | 27.7% | (59 / 213) | 0% | (0 / 67) | 0% | (0 / 25) | 27.7% | (59 / 213) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/connectors/mqtt/ | 23.74% | (33 / 139) | 0% | (0 / 66) | 0% | (0 / 10) | 27.97% | (33 / 118) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/filters/handler/ | 33.77% | (26 / 77) | 0% | (0 / 24) | 0% | (0 / 18) | 33.77% | (26 / 77) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/filters/rpc/ | 37.84% | (14 / 37) | 0% | (0 / 16) | 0% | (0 / 7) | 37.84% | (14 / 37) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/master/ | 17.39% | (48 / 276) | 0% | (0 / 103) | 0% | (0 / 41) | 17.39% | (48 / 276) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/modules/ | 16.24% | (69 / 425) | 0% | (0 / 150) | 0% | (0 / 73) | 16.24% | (69 / 425) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/monitor/ | 26.19% | (11 / 42) | 0% | (0 / 6) | 0% | (0 / 11) | 26.19% | (11 / 42) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/pushSchedulers/ | 18.37% | (18 / 98) | 0% | (0 / 52) | 0% | (0 / 21) | 18.37% | (18 / 98) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/server/ | 17.19% | (38 / 221) | 0% | (0 / 81) | 0% | (0 / 34) | 17.19% | (38 / 221) | |
| node-npmtest-pomelo/node_modules/pomelo/lib/util/ | 23.23% | (105 / 452) | 3.03% | (7 / 231) | 14.04% | (8 / 57) | 23.33% | (105 / 450) | |
| node-npmtest-pomelo/node_modules/pomelo/template/game-server/ | 25% | (2 / 8) | 100% | (0 / 0) | 0% | (0 / 2) | 25% | (2 / 8) |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| example.js | 100% | (83 / 83) | 100% | (73 / 73) | 100% | (12 / 12) | 100% | (83 / 83) | |
| lib.npmtest_pomelo.js | 100% | (16 / 16) | 100% | (14 / 14) | 100% | (3 / 3) | 100% | (16 / 16) | |
| test.js | 100% | (54 / 54) | 100% | (39 / 39) | 100% | (13 / 13) | 100% | (54 / 54) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 2 3 3 3 3 1 3 3 3 1 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*
example.js
quickstart example
instruction
1. save this script as example.js
2. run the shell command:
$ npm install npmtest-pomelo && PORT=8081 node example.js
3. play with the browser-demo on http://127.0.0.1:8081
*/
/* istanbul instrument in package npmtest_pomelo */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
// init utility2_rollup
local = local.global.utility2_rollup || (local.modeJs === 'browser'
? local.global.utility2_npmtest_pomelo
: global.utility2_moduleExports);
// export local
local.global.local = local;
}());
switch (local.modeJs) {
// post-init
// run browser js-env code - post-init
/* istanbul ignore next */
case 'browser':
local.testRunBrowser = function (event) {
Eif (!event || (event &&
event.currentTarget &&
event.currentTarget.className &&
event.currentTarget.className.includes &&
event.currentTarget.className.includes('onreset'))) {
// reset output
Array.from(
document.querySelectorAll('body > .resettable')
).forEach(function (element) {
switch (element.tagName) {
case 'INPUT':
case 'TEXTAREA':
element.value = '';
break;
default:
element.textContent = '';
}
});
}
switch (event && event.currentTarget && event.currentTarget.id) {
case 'testRunButton1':
// show tests
Eif (document.querySelector('#testReportDiv1').style.display === 'none') {
document.querySelector('#testReportDiv1').style.display = 'block';
document.querySelector('#testRunButton1').textContent =
'hide internal test';
local.modeTest = true;
local.testRunDefault(local);
// hide tests
} else {
document.querySelector('#testReportDiv1').style.display = 'none';
document.querySelector('#testRunButton1').textContent = 'run internal test';
}
break;
// custom-case
default:
break;
}
Iif (document.querySelector('#inputTextareaEval1') && (!event || (event &&
event.currentTarget &&
event.currentTarget.className &&
event.currentTarget.className.includes &&
event.currentTarget.className.includes('oneval')))) {
// try to eval input-code
try {
/*jslint evil: true*/
eval(document.querySelector('#inputTextareaEval1').value);
} catch (errorCaught) {
console.error(errorCaught);
}
}
};
// log stderr and stdout to #outputTextareaStdout1
['error', 'log'].forEach(function (key) {
console[key + '_original'] = console[key];
console[key] = function () {
var element;
console[key + '_original'].apply(console, arguments);
element = document.querySelector('#outputTextareaStdout1');
Iif (!element) {
return;
}
// append text to #outputTextareaStdout1
element.value += Array.from(arguments).map(function (arg) {
return typeof arg === 'string'
? arg
: JSON.stringify(arg, null, 4);
}).join(' ') + '\n';
// scroll textarea to bottom
element.scrollTop = element.scrollHeight;
};
});
// init event-handling
['change', 'click', 'keyup'].forEach(function (event) {
Array.from(document.querySelectorAll('.on' + event)).forEach(function (element) {
element.addEventListener(event, local.testRunBrowser);
});
});
// run tests
local.testRunBrowser();
break;
// run node js-env code - post-init
/* istanbul ignore next */
case 'node':
// export local
module.exports = local;
// require modules
local.fs = require('fs');
local.http = require('http');
local.url = require('url');
// init assets
local.assetsDict = local.assetsDict || {};
/* jslint-ignore-begin */
local.assetsDict['/assets.index.template.html'] = '\
<!doctype html>\n\
<html lang="en">\n\
<head>\n\
<meta charset="UTF-8">\n\
<meta name="viewport" content="width=device-width, initial-scale=1">\n\
<title>{{env.npm_package_name}} (v{{env.npm_package_version}})</title>\n\
<style>\n\
/*csslint\n\
box-sizing: false,\n\
universal-selector: false\n\
*/\n\
* {\n\
box-sizing: border-box;\n\
}\n\
body {\n\
background: #dde;\n\
font-family: Arial, Helvetica, sans-serif;\n\
margin: 2rem;\n\
}\n\
body > * {\n\
margin-bottom: 1rem;\n\
}\n\
.utility2FooterDiv {\n\
margin-top: 20px;\n\
text-align: center;\n\
}\n\
</style>\n\
<style>\n\
/*csslint\n\
*/\n\
textarea {\n\
font-family: monospace;\n\
height: 10rem;\n\
width: 100%;\n\
}\n\
textarea[readonly] {\n\
background: #ddd;\n\
}\n\
</style>\n\
</head>\n\
<body>\n\
<!-- utility2-comment\n\
<div id="ajaxProgressDiv1" style="background: #d00; height: 2px; left: 0; margin: 0; padding: 0; position: fixed; top: 0; transition: background 0.5s, width 1.5s; width: 25%;"></div>\n\
utility2-comment -->\n\
<h1>\n\
<!-- utility2-comment\n\
<a\n\
{{#if env.npm_package_homepage}}\n\
href="{{env.npm_package_homepage}}"\n\
{{/if env.npm_package_homepage}}\n\
target="_blank"\n\
>\n\
utility2-comment -->\n\
{{env.npm_package_name}} (v{{env.npm_package_version}})\n\
<!-- utility2-comment\n\
</a>\n\
utility2-comment -->\n\
</h1>\n\
<h3>{{env.npm_package_description}}</h3>\n\
<!-- utility2-comment\n\
<h4><a download href="assets.app.js">download standalone app</a></h4>\n\
<button class="onclick onreset" id="testRunButton1">run internal test</button><br>\n\
<div id="testReportDiv1" style="display: none;"></div>\n\
utility2-comment -->\n\
\n\
\n\
\n\
<label>stderr and stdout</label>\n\
<textarea class="resettable" id="outputTextareaStdout1" readonly></textarea>\n\
<!-- utility2-comment\n\
{{#if isRollup}}\n\
<script src="assets.app.js"></script>\n\
{{#unless isRollup}}\n\
utility2-comment -->\n\
<script src="assets.utility2.rollup.js"></script>\n\
<script src="jsonp.utility2._stateInit?callback=window.utility2._stateInit"></script>\n\
<script src="assets.npmtest_pomelo.rollup.js"></script>\n\
<script src="assets.example.js"></script>\n\
<script src="assets.test.js"></script>\n\
<!-- utility2-comment\n\
{{/if isRollup}}\n\
utility2-comment -->\n\
<div class="utility2FooterDiv">\n\
[ this app was created with\n\
<a href="https://github.com/kaizhu256/node-utility2" target="_blank">utility2</a>\n\
]\n\
</div>\n\
</body>\n\
</html>\n\
';
/* jslint-ignore-end */
Iif (local.templateRender) {
local.assetsDict['/'] = local.templateRender(
local.assetsDict['/assets.index.template.html'],
{
env: local.objectSetDefault(local.env, {
npm_package_description: 'the greatest app in the world!',
npm_package_name: 'my-app',
npm_package_nameAlias: 'my_app',
npm_package_version: '0.0.1'
})
}
);
} else {
local.assetsDict['/'] = local.assetsDict['/assets.index.template.html']
.replace((/\{\{env\.(\w+?)\}\}/g), function (match0, match1) {
// jslint-hack
String(match0);
switch (match1) {
case 'npm_package_description':
return 'the greatest app in the world!';
case 'npm_package_name':
return 'my-app';
case 'npm_package_nameAlias':
return 'my_app';
case 'npm_package_version':
return '0.0.1';
}
});
}
// run the cli
Eif (local.global.utility2_rollup || module !== require.main) {
break;
}
local.assetsDict['/assets.example.js'] =
local.assetsDict['/assets.example.js'] ||
local.fs.readFileSync(__filename, 'utf8');
// bug-workaround - long $npm_package_buildCustomOrg
/* jslint-ignore-begin */
local.assetsDict['/assets.npmtest_pomelo.rollup.js'] =
local.assetsDict['/assets.npmtest_pomelo.rollup.js'] ||
local.fs.readFileSync(
local.npmtest_pomelo.__dirname + '/lib.npmtest_pomelo.js',
'utf8'
).replace((/^#!/), '//');
/* jslint-ignore-end */
local.assetsDict['/favicon.ico'] = local.assetsDict['/favicon.ico'] || '';
// if $npm_config_timeout_exit exists,
// then exit this process after $npm_config_timeout_exit ms
if (Number(process.env.npm_config_timeout_exit)) {
setTimeout(process.exit, Number(process.env.npm_config_timeout_exit));
}
// start server
if (local.global.utility2_serverHttp1) {
break;
}
process.env.PORT = process.env.PORT || '8081';
console.error('server starting on port ' + process.env.PORT);
local.http.createServer(function (request, response) {
request.urlParsed = local.url.parse(request.url);
if (local.assetsDict[request.urlParsed.pathname] !== undefined) {
response.end(local.assetsDict[request.urlParsed.pathname]);
return;
}
response.statusCode = 404;
response.end();
}).listen(process.env.PORT);
break;
}
}());
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 2 2 2 2 2 2 2 1 2 2 2 2 1 1 1 1 | /* istanbul instrument in package npmtest_pomelo */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
// init utility2_rollup
local = local.global.utility2_rollup || local;
// init lib
local.local = local.npmtest_pomelo = local;
// init exports
if (local.modeJs === 'browser') {
local.global.utility2_npmtest_pomelo = local;
} else {
module.exports = local;
module.exports.__dirname = __dirname;
module.exports.module = module;
}
}());
}());
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | 2 2 2 2 2 2 2 1 2 2 1 1 1 1 2 2 2 2 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 2 1 2 2 1 2 2 1 1 1 1 1 | /* istanbul instrument in package npmtest_pomelo */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
switch (local.modeJs) {
// re-init local from window.local
case 'browser':
local = local.global.utility2.objectSetDefault(
local.global.utility2_rollup || local.global.local,
local.global.utility2
);
break;
// re-init local from example.js
case 'node':
local = (local.global.utility2_rollup || require('utility2'))
.requireExampleJsFromReadme();
break;
}
// export local
local.global.local = local;
}());
// run shared js-env code - function
(function () {
return;
}());
switch (local.modeJs) {
// run browser js-env code - function
case 'browser':
break;
// run node js-env code - function
case 'node':
break;
}
// run shared js-env code - post-init
(function () {
return;
}());
switch (local.modeJs) {
// run browser js-env code - post-init
case 'browser':
local.testCase_browser_nullCase = local.testCase_browser_nullCase || function (
options,
onError
) {
/*
* this function will test browsers's null-case handling-behavior-behavior
*/
onError(null, options);
};
// run tests
local.nop(local.modeTest &&
document.querySelector('#testRunButton1') &&
document.querySelector('#testRunButton1').click());
break;
// run node js-env code - post-init
/* istanbul ignore next */
case 'node':
local.testCase_buildApidoc_default = local.testCase_buildApidoc_default || function (
options,
onError
) {
/*
* this function will test buildApidoc's default handling-behavior-behavior
*/
options = { modulePathList: module.paths };
local.buildApidoc(options, onError);
};
local.testCase_buildApp_default = local.testCase_buildApp_default || function (
options,
onError
) {
/*
* this function will test buildApp's default handling-behavior-behavior
*/
local.testCase_buildReadme_default(options, local.onErrorThrow);
local.testCase_buildLib_default(options, local.onErrorThrow);
local.testCase_buildTest_default(options, local.onErrorThrow);
local.testCase_buildCustomOrg_default(options, local.onErrorThrow);
options = [];
local.buildApp(options, onError);
};
local.testCase_buildCustomOrg_default = local.testCase_buildCustomOrg_default ||
function (options, onError) {
/*
* this function will test buildCustomOrg's default handling-behavior
*/
options = {};
local.buildCustomOrg(options, onError);
};
local.testCase_buildLib_default = local.testCase_buildLib_default || function (
options,
onError
) {
/*
* this function will test buildLib's default handling-behavior
*/
options = {};
local.buildLib(options, onError);
};
local.testCase_buildReadme_default = local.testCase_buildReadme_default || function (
options,
onError
) {
/*
* this function will test buildReadme's default handling-behavior-behavior
*/
options = {};
local.buildReadme(options, onError);
};
local.testCase_buildTest_default = local.testCase_buildTest_default || function (
options,
onError
) {
/*
* this function will test buildTest's default handling-behavior
*/
options = {};
local.buildTest(options, onError);
};
local.testCase_webpage_default = local.testCase_webpage_default || function (
options,
onError
) {
/*
* this function will test webpage's default handling-behavior
*/
options = { modeCoverageMerge: true, url: local.serverLocalHost + '?modeTest=1' };
local.browserTest(options, onError);
};
// run test-server
local.testRunServer(local);
break;
}
}());
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 92.31% | (12 / 13) | 75% | (3 / 4) | 50% | (1 / 2) | 92.31% | (12 / 13) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 1 1 1 1 1 1 1 6 6 6 6 5 | var fs = require('fs');
var consoleService = require('./lib/consoleService');
module.exports.createMasterConsole = consoleService.createMasterConsole;
module.exports.createMonitorConsole = consoleService.createMonitorConsole;
module.exports.adminClient = require('./lib/client/client');
exports.modules = {};
fs.readdirSync(__dirname + '/lib/modules').forEach(function(filename) {
Eif (/\.js$/.test(filename)) {
var name = filename.substr(0, filename.lastIndexOf('.'));
var _module = require('./lib/modules/' + name);
if (!_module.moduleError) {
exports.modules.__defineGetter__(name, function() {
return _module;
});
}
}
});
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| consoleService.js | 14.56% | (30 / 206) | 0% | (0 / 114) | 0% | (0 / 25) | 14.56% | (30 / 206) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'ConsoleService');
var MonitorAgent = require('./monitor/monitorAgent');
var EventEmitter = require('events').EventEmitter;
var MasterAgent = require('./master/masterAgent');
var schedule = require('pomelo-scheduler');
var protocol = require('./util/protocol');
var utils = require('./util/utils');
var util = require('util');
var MS_OF_SECOND = 1000;
/**
* ConsoleService Constructor
*
* @class ConsoleService
* @constructor
* @param {Object} opts construct parameter
* opts.type {String} server type, 'master', 'connector', etc.
* opts.id {String} server id
* opts.host {String} (monitor only) master server host
* opts.port {String | Number} listen port for master or master port for monitor
* opts.master {Boolean} current service is master or monitor
* opts.info {Object} more server info for current server, {id, serverType, host, port}
* @api public
*/
var ConsoleService = function(opts) {
EventEmitter.call(this);
this.port = opts.port;
this.env = opts.env;
this.values = {};
this.master = opts.master;
this.modules = {};
this.commands = {
'list': listCommand,
'enable': enableCommand,
'disable': disableCommand
};
if (this.master) {
this.authUser = opts.authUser || utils.defaultAuthUser;
this.authServer = opts.authServer || utils.defaultAuthServerMaster;
this.agent = new MasterAgent(this, opts);
} else {
this.type = opts.type;
this.id = opts.id;
this.host = opts.host;
this.authServer = opts.authServer || utils.defaultAuthServerMonitor;
this.agent = new MonitorAgent({
consoleService: this,
id: this.id,
type: this.type,
info: opts.info
});
}
};
util.inherits(ConsoleService, EventEmitter);
/**
* start master or monitor
*
* @param {Function} cb callback function
* @api public
*/
ConsoleService.prototype.start = function(cb) {
if (this.master) {
var self = this;
this.agent.listen(this.port, function(err) {
if (!!err) {
utils.invokeCallback(cb, err);
return;
}
exportEvent(self, self.agent, 'register');
exportEvent(self, self.agent, 'disconnect');
exportEvent(self, self.agent, 'reconnect');
process.nextTick(function() {
utils.invokeCallback(cb);
});
});
} else {
logger.info('try to connect master: %j, %j, %j', this.type, this.host, this.port);
this.agent.connect(this.port, this.host, cb);
exportEvent(this, this.agent, 'close');
}
exportEvent(this, this.agent, 'error');
for (var mid in this.modules) {
this.enable(mid);
}
};
/**
* stop console modules and stop master server
*
* @api public
*/
ConsoleService.prototype.stop = function() {
for (var mid in this.modules) {
this.disable(mid);
}
this.agent.close();
};
/**
* register a new adminConsole module
*
* @param {String} moduleId adminConsole id/name
* @param {Object} module module object
* @api public
*/
ConsoleService.prototype.register = function(moduleId, module) {
this.modules[moduleId] = registerRecord(this, moduleId, module);
};
/**
* enable adminConsole module
*
* @param {String} moduleId adminConsole id/name
* @api public
*/
ConsoleService.prototype.enable = function(moduleId) {
var record = this.modules[moduleId];
if (record && !record.enable) {
record.enable = true;
addToSchedule(this, record);
return true;
}
return false;
};
/**
* disable adminConsole module
*
* @param {String} moduleId adminConsole id/name
* @api public
*/
ConsoleService.prototype.disable = function(moduleId) {
var record = this.modules[moduleId];
if (record && record.enable) {
record.enable = false;
if (record.schedule && record.jobId) {
schedule.cancelJob(record.jobId);
schedule.jobId = null;
}
return true;
}
return false;
};
/**
* call concrete module and handler(monitorHandler,masterHandler,clientHandler)
*
* @param {String} moduleId adminConsole id/name
* @param {String} method handler
* @param {Object} msg message
* @param {Function} cb callback function
* @api public
*/
ConsoleService.prototype.execute = function(moduleId, method, msg, cb) {
var self = this;
var m = this.modules[moduleId];
if (!m) {
logger.error('unknown module: %j.', moduleId);
cb('unknown moduleId:' + moduleId);
return;
}
if (!m.enable) {
logger.error('module %j is disable.', moduleId);
cb('module ' + moduleId + ' is disable');
return;
}
var module = m.module;
if (!module || typeof module[method] !== 'function') {
logger.error('module %j dose not have a method called %j.', moduleId, method);
cb('module ' + moduleId + ' dose not have a method called ' + method);
return;
}
var log = {
action: 'execute',
moduleId: moduleId,
method: method,
msg: msg
}
var aclMsg = aclControl(self.agent, 'execute', method, moduleId, msg);
if (aclMsg !== 0 && aclMsg !== 1) {
log['error'] = aclMsg;
self.emit('admin-log', log, aclMsg);
cb(new Error(aclMsg), null);
return;
}
if (method === 'clientHandler') {
self.emit('admin-log', log);
}
module[method](this.agent, msg, cb);
};
ConsoleService.prototype.command = function(command, moduleId, msg, cb) {
var self = this;
var fun = this.commands[command];
if (!fun || typeof fun !== 'function') {
cb('unknown command:' + command);
return;
}
var log = {
action: 'command',
moduleId: moduleId,
msg: msg
}
var aclMsg = aclControl(self.agent, 'command', null, moduleId, msg);
if (aclMsg !== 0 && aclMsg !== 1) {
log['error'] = aclMsg;
self.emit('admin-log', log, aclMsg);
cb(new Error(aclMsg), null);
return;
}
self.emit('admin-log', log);
fun(this, moduleId, msg, cb);
}
/**
* set module data to a map
*
* @param {String} moduleId adminConsole id/name
* @param {Object} value module data
* @api public
*/
ConsoleService.prototype.set = function(moduleId, value) {
this.values[moduleId] = value;
};
/**
* get module data from map
*
* @param {String} moduleId adminConsole id/name
* @api public
*/
ConsoleService.prototype.get = function(moduleId) {
return this.values[moduleId];
};
/**
* register a module service
*
* @param {Object} service consoleService object
* @param {String} moduleId adminConsole id/name
* @param {Object} module module object
* @api private
*/
var registerRecord = function(service, moduleId, module) {
var record = {
moduleId: moduleId,
module: module,
enable: false
};
if (module.type && module.interval) {
if (!service.master && record.module.type === 'push' || service.master && record.module.type !== 'push') {
// push for monitor or pull for master(default)
record.delay = module.delay || 0;
record.interval = module.interval || 1;
// normalize the arguments
if (record.delay < 0) {
record.delay = 0;
}
if (record.interval < 0) {
record.interval = 1;
}
record.interval = Math.ceil(record.interval);
record.delay *= MS_OF_SECOND;
record.interval *= MS_OF_SECOND;
record.schedule = true;
}
}
return record;
};
/**
* schedule console module
*
* @param {Object} service consoleService object
* @param {Object} record module object
* @api private
*/
var addToSchedule = function(service, record) {
if (record && record.schedule) {
record.jobId = schedule.scheduleJob({
start: Date.now() + record.delay,
period: record.interval
},
doScheduleJob, {
service: service,
record: record
});
}
};
/**
* run schedule job
*
* @param {Object} args argments
* @api private
*/
var doScheduleJob = function(args) {
var service = args.service;
var record = args.record;
if (!service || !record || !record.module || !record.enable) {
return;
}
if (service.master) {
record.module.masterHandler(service.agent, null, function(err) {
logger.error('interval push should not have a callback.');
});
} else {
record.module.monitorHandler(service.agent, null, function(err) {
logger.error('interval push should not have a callback.');
});
}
};
/**
* export closure function out
*
* @param {Function} outer outer function
* @param {Function} inner inner function
* @param {object} event
* @api private
*/
var exportEvent = function(outer, inner, event) {
inner.on(event, function() {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(event);
outer.emit.apply(outer, args);
});
};
/**
* List current modules
*/
var listCommand = function(consoleService, moduleId, msg, cb) {
var modules = consoleService.modules;
var result = [];
for (var moduleId in modules) {
if (/^__\w+__$/.test(moduleId)) {
continue;
}
result.push(moduleId);
}
cb(null, {
modules: result
});
};
/**
* enable module in current server
*/
var enableCommand = function(consoleService, moduleId, msg, cb) {
if (!moduleId) {
logger.error('fail to enable admin module for ' + moduleId);
cb('empty moduleId');
return;
}
var modules = consoleService.modules;
if (!modules[moduleId]) {
cb(null, protocol.PRO_FAIL);
return;
}
if (consoleService.master) {
consoleService.enable(moduleId);
consoleService.agent.notifyCommand("enable", moduleId, msg);
cb(null, protocol.PRO_OK);
} else {
consoleService.enable(moduleId);
cb(null, protocol.PRO_OK);
}
};
/**
* disable module in current server
*/
var disableCommand = function(consoleService, moduleId, msg, cb) {
if (!moduleId) {
logger.error('fail to enable admin module for ' + moduleId);
cb('empty moduleId');
return;
}
var modules = consoleService.modules;
if (!modules[moduleId]) {
cb(null, protocol.PRO_FAIL);
return;
}
if (consoleService.master) {
consoleService.disable(moduleId);
consoleService.agent.notifyCommand("disable", moduleId, msg);
cb(null, protocol.PRO_OK);
} else {
consoleService.disable(moduleId);
cb(null, protocol.PRO_OK);
}
};
var aclControl = function(agent, action, method, moduleId, msg) {
if (action === 'execute') {
if (method !== 'clientHandler' || moduleId !== '__console__') {
return 0;
}
var signal = msg.signal;
if (!signal || !(signal === 'stop' || signal === 'add' || signal === 'kill')) {
return 0;
}
}
var clientId = msg.clientId;
if (!clientId) {
return 'Unknow clientId';
}
var _client = agent.getClientById(clientId);
if (_client && _client.info && _client.info.level) {
var level = _client.info.level;
if (level > 1) {
return 'Command permission denied';
}
} else {
return 'Client info error';
}
return 1;
}
/**
* Create master ConsoleService
*
* @param {Object} opts construct parameter
* opts.port {String | Number} listen port for master console
*/
module.exports.createMasterConsole = function(opts) {
opts = opts || {};
opts.master = true;
return new ConsoleService(opts);
};
/**
* Create monitor ConsoleService
*
* @param {Object} opts construct parameter
* opts.type {String} server type, 'master', 'connector', etc.
* opts.id {String} server id
* opts.host {String} master server host
* opts.port {String | Number} master port
*/
module.exports.createMonitorConsole = function(opts) {
return new ConsoleService(opts);
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| client.js | 12.05% | (10 / 83) | 0% | (0 / 36) | 0% | (0 / 13) | 12.05% | (10 / 83) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- commandLine Client
* Copyright(c) 2015 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var MqttClient = require('../protocol/mqtt/mqttClient');
var protocol = require('../util/protocol');
// var io = require('socket.io-client');
var utils = require('../util/utils');
var Client = function(opt) {
this.id = "";
this.reqId = 1;
this.callbacks = {};
this.listeners = {};
this.state = Client.ST_INITED;
this.socket = null;
opt = opt || {};
this.username = opt['username'] || "";
this.password = opt['password'] || "";
this.md5 = opt['md5'] || false;
};
Client.prototype = {
connect: function(id, host, port, cb) {
this.id = id;
var self = this;
console.log('try to connect ' + host + ':' + port);
this.socket = new MqttClient({
id: id
});
this.socket.connect(host, port);
// this.socket = io.connect('http://' + host + ':' + port, {
// 'force new connection': true,
// 'reconnect': false
// });
this.socket.on('connect', function() {
self.state = Client.ST_CONNECTED;
if (self.md5) {
self.password = utils.md5(self.password);
}
self.doSend('register', {
type: "client",
id: id,
username: self.username,
password: self.password,
md5: self.md5
});
});
this.socket.on('register', function(res) {
if (res.code !== protocol.PRO_OK) {
cb(res.msg);
return;
}
self.state = Client.ST_REGISTERED;
cb();
});
this.socket.on('client', function(msg) {
msg = protocol.parse(msg);
if (msg.respId) {
// response for request
var cb = self.callbacks[msg.respId];
delete self.callbacks[msg.respId];
if (cb && typeof cb === 'function') {
cb(msg.error, msg.body);
}
} else if (msg.moduleId) {
// notify
self.emit(msg.moduleId, msg);
}
});
this.socket.on('error', function(err) {
if (self.state < Client.ST_CONNECTED) {
cb(err);
}
self.emit('error', err);
});
this.socket.on('disconnect', function(reason) {
this.state = Client.ST_CLOSED;
self.emit('close');
});
},
request: function(moduleId, msg, cb) {
var id = this.reqId++;
// something dirty: attach current client id into msg
msg = msg || {};
msg.clientId = this.id;
msg.username = this.username;
var req = protocol.composeRequest(id, moduleId, msg);
this.callbacks[id] = cb;
this.doSend('client', req);
// this.socket.emit('client', req);
},
notify: function(moduleId, msg) {
// something dirty: attach current client id into msg
msg = msg || {};
msg.clientId = this.id;
msg.username = this.username;
var req = protocol.composeRequest(null, moduleId, msg);
this.doSend('client', req);
// this.socket.emit('client', req);
},
command: function(command, moduleId, msg, cb) {
var id = this.reqId++;
msg = msg || {};
msg.clientId = this.id;
msg.username = this.username;
var commandReq = protocol.composeCommand(id, command, moduleId, msg);
this.callbacks[id] = cb;
this.doSend('client', commandReq);
// this.socket.emit('client', commandReq);
},
doSend: function(topic, msg) {
this.socket.send(topic, msg);
},
on: function(event, listener) {
this.listeners[event] = this.listeners[event] || [];
this.listeners[event].push(listener);
},
emit: function(event) {
var listeners = this.listeners[event];
if (!listeners || !listeners.length) {
return;
}
var args = Array.prototype.slice.call(arguments, 1);
var listener;
for (var i = 0, l = listeners.length; i < l; i++) {
listener = listeners[i];
if (typeof listener === 'function') {
listener.apply(null, args);
}
}
}
};
Client.ST_INITED = 1;
Client.ST_CONNECTED = 2;
Client.ST_REGISTERED = 3;
Client.ST_CLOSED = 4;
module.exports = Client;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| masterAgent.js | 15.21% | (40 / 263) | 0% | (0 / 96) | 0% | (0 / 39) | 15.21% | (40 / 263) | |
| masterSocket.js | 8.05% | (12 / 149) | 0% | (0 / 62) | 0% | (0 / 13) | 8.05% | (12 / 149) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MasterAgent');
var MqttServer = require('../protocol/mqtt/mqttServer');
var EventEmitter = require('events').EventEmitter;
var MasterSocket = require('./masterSocket');
var protocol = require('../util/protocol');
var utils = require('../util/utils');
var Util = require('util');
var ST_INITED = 1;
var ST_STARTED = 2;
var ST_CLOSED = 3;
/**
* MasterAgent Constructor
*
* @class MasterAgent
* @constructor
* @param {Object} opts construct parameter
* opts.consoleService {Object} consoleService
* opts.id {String} server id
* opts.type {String} server type, 'master', 'connector', etc.
* opts.socket {Object} socket-io object
* opts.reqId {Number} reqId add by 1
* opts.callbacks {Object} callbacks
* opts.state {Number} MasterAgent state
* @api public
*/
var MasterAgent = function(consoleService, opts) {
EventEmitter.call(this);
this.reqId = 1;
this.idMap = {};
this.msgMap = {};
this.typeMap = {};
this.clients = {};
this.sockets = {};
this.slaveMap = {};
this.server = null;
this.callbacks = {};
this.state = ST_INITED;
this.whitelist = opts.whitelist;
this.consoleService = consoleService;
};
Util.inherits(MasterAgent, EventEmitter);
/**
* master listen to a port and handle register and request
*
* @param {String} port
* @api public
*/
MasterAgent.prototype.listen = function(port, cb) {
if (this.state > ST_INITED) {
logger.error('master agent has started or closed.');
return;
}
this.state = ST_STARTED;
this.server = new MqttServer();
this.server.listen(port);
// this.server = sio.listen(port);
// this.server.set('log level', 0);
cb = cb || function() {}
var self = this;
this.server.on('error', function(err) {
self.emit('error', err);
cb(err);
});
this.server.once('listening', function() {
setImmediate(function() {
cb();
});
});
this.server.on('connection', function(socket) {
// var id, type, info, registered, username;
var masterSocket = new MasterSocket();
masterSocket['agent'] = self;
masterSocket['socket'] = socket;
self.sockets[socket.id] = socket;
socket.on('register', function(msg) {
// register a new connection
masterSocket.onRegister(msg);
}); // end of on 'register'
// message from monitor
socket.on('monitor', function(msg) {
masterSocket.onMonitor(msg);
}); // end of on 'monitor'
// message from client
socket.on('client', function(msg) {
masterSocket.onClient(msg);
}); // end of on 'client'
socket.on('reconnect', function(msg) {
masterSocket.onReconnect(msg);
});
socket.on('disconnect', function() {
masterSocket.onDisconnect();
});
socket.on('close', function() {
masterSocket.onDisconnect();
});
socket.on('error', function(err) {
masterSocket.onError(err);
});
}); // end of on 'connection'
}; // end of listen
/**
* close master agent
*
* @api public
*/
MasterAgent.prototype.close = function() {
if (this.state > ST_STARTED) {
return;
}
this.state = ST_CLOSED;
this.server.close();
};
/**
* set module
*
* @param {String} moduleId module id/name
* @param {Object} value module object
* @api public
*/
MasterAgent.prototype.set = function(moduleId, value) {
this.consoleService.set(moduleId, value);
};
/**
* get module
*
* @param {String} moduleId module id/name
* @api public
*/
MasterAgent.prototype.get = function(moduleId) {
return this.consoleService.get(moduleId);
};
/**
* getClientById
*
* @param {String} clientId
* @api public
*/
MasterAgent.prototype.getClientById = function(clientId) {
return this.clients[clientId];
};
/**
* request monitor{master node} data from monitor
*
* @param {String} serverId
* @param {String} moduleId module id/name
* @param {Object} msg
* @param {Function} callback function
* @api public
*/
MasterAgent.prototype.request = function(serverId, moduleId, msg, cb) {
if (this.state > ST_STARTED) {
return false;
}
cb = cb || function() {}
var curId = this.reqId++;
this.callbacks[curId] = cb;
if (!this.msgMap[serverId]) {
this.msgMap[serverId] = {};
}
this.msgMap[serverId][curId] = {
moduleId: moduleId,
msg: msg
}
var record = this.idMap[serverId];
if (!record) {
cb(new Error('unknown server id:' + serverId));
return false;
}
sendToMonitor(record.socket, curId, moduleId, msg);
return true;
};
/**
* request server data from monitor by serverInfo{host:port}
*
* @param {String} serverId
* @param {Object} serverInfo
* @param {String} moduleId module id/name
* @param {Object} msg
* @param {Function} callback function
* @api public
*/
MasterAgent.prototype.requestServer = function(serverId, serverInfo, moduleId, msg, cb) {
if (this.state > ST_STARTED) {
return false;
}
var record = this.idMap[serverId];
if (!record) {
utils.invokeCallback(cb, new Error('unknown server id:' + serverId));
return false;
}
var curId = this.reqId++;
this.callbacks[curId] = cb;
if (utils.compareServer(record, serverInfo)) {
sendToMonitor(record.socket, curId, moduleId, msg);
} else {
var slaves = this.slaveMap[serverId];
for (var i = 0, l = slaves.length; i < l; i++) {
if (utils.compareServer(slaves[i], serverInfo)) {
sendToMonitor(slaves[i].socket, curId, moduleId, msg);
break;
}
}
}
return true;
};
/**
* notify a monitor{master node} by id without callback
*
* @param {String} serverId
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifyById = function(serverId, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
var record = this.idMap[serverId];
if (!record) {
logger.error('fail to notifyById for unknown server id:' + serverId);
return false;
}
sendToMonitor(record.socket, null, moduleId, msg);
return true;
};
/**
* notify a monitor by server{host:port} without callback
*
* @param {String} serverId
* @param {Object} serverInfo{host:port}
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifyByServer = function(serverId, serverInfo, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
var record = this.idMap[serverId];
if (!record) {
logger.error('fail to notifyByServer for unknown server id:' + serverId);
return false;
}
if (utils.compareServer(record, serverInfo)) {
sendToMonitor(record.socket, null, moduleId, msg);
} else {
var slaves = this.slaveMap[serverId];
for (var i = 0, l = slaves.length; i < l; i++) {
if (utils.compareServer(slaves[i], serverInfo)) {
sendToMonitor(slaves[i].socket, null, moduleId, msg);
break;
}
}
}
return true;
};
/**
* notify slaves by id without callback
*
* @param {String} serverId
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifySlavesById = function(serverId, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
var slaves = this.slaveMap[serverId];
if (!slaves || slaves.length === 0) {
logger.error('fail to notifySlavesById for unknown server id:' + serverId);
return false;
}
broadcastMonitors(slaves, moduleId, msg);
return true;
};
/**
* notify monitors by type without callback
*
* @param {String} type serverType
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifyByType = function(type, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
var list = this.typeMap[type];
if (!list || list.length === 0) {
logger.error('fail to notifyByType for unknown server type:' + type);
return false;
}
broadcastMonitors(list, moduleId, msg);
return true;
};
/**
* notify all the monitors without callback
*
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifyAll = function(moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
broadcastMonitors(this.idMap, moduleId, msg);
return true;
};
/**
* notify a client by id without callback
*
* @param {String} clientId
* @param {String} moduleId module id/name
* @param {Object} msg
* @api public
*/
MasterAgent.prototype.notifyClient = function(clientId, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
var record = this.clients[clientId];
if (!record) {
logger.error('fail to notifyClient for unknown client id:' + clientId);
return false;
}
sendToClient(record.socket, null, moduleId, msg);
};
MasterAgent.prototype.notifyCommand = function(command, moduleId, msg) {
if (this.state > ST_STARTED) {
return false;
}
broadcastCommand(this.idMap, command, moduleId, msg);
return true;
};
/**
* add monitor,client to connection -- idMap
*
* @param {Object} agent agent object
* @param {String} id
* @param {String} type serverType
* @param {Object} socket socket-io object
* @api private
*/
var addConnection = function(agent, id, type, pid, info, socket) {
var record = {
id: id,
type: type,
pid: pid,
info: info,
socket: socket
};
if (type === 'client') {
agent.clients[id] = record;
} else {
if (!agent.idMap[id]) {
agent.idMap[id] = record;
var list = agent.typeMap[type] = agent.typeMap[type] || [];
list.push(record);
} else {
var slaves = agent.slaveMap[id] = agent.slaveMap[id] || [];
slaves.push(record);
}
}
return record;
};
/**
* remove monitor,client connection -- idMap
*
* @param {Object} agent agent object
* @param {String} id
* @param {String} type serverType
* @api private
*/
var removeConnection = function(agent, id, type, info) {
if (type === 'client') {
delete agent.clients[id];
} else {
// remove master node in idMap and typeMap
var record = agent.idMap[id];
if (!record) {
return;
}
var _info = record['info']; // info {host, port}
if (utils.compareServer(_info, info)) {
delete agent.idMap[id];
var list = agent.typeMap[type];
if (list) {
for (var i = 0, l = list.length; i < l; i++) {
if (list[i].id === id) {
list.splice(i, 1);
break;
}
}
if (list.length === 0) {
delete agent.typeMap[type];
}
}
} else {
// remove slave node in slaveMap
var slaves = agent.slaveMap[id];
if (slaves) {
for (var i = 0, l = slaves.length; i < l; i++) {
if (utils.compareServer(slaves[i]['info'], info)) {
slaves.splice(i, 1);
break;
}
}
if (slaves.length === 0) {
delete agent.slaveMap[id];
}
}
}
}
};
/**
* send msg to monitor
*
* @param {Object} socket socket-io object
* @param {Number} reqId request id
* @param {String} moduleId module id/name
* @param {Object} msg message
* @api private
*/
var sendToMonitor = function(socket, reqId, moduleId, msg) {
doSend(socket, 'monitor', protocol.composeRequest(reqId, moduleId, msg));
};
/**
* send msg to client
*
* @param {Object} socket socket-io object
* @param {Number} reqId request id
* @param {String} moduleId module id/name
* @param {Object} msg message
* @api private
*/
var sendToClient = function(socket, reqId, moduleId, msg) {
doSend(socket, 'client', protocol.composeRequest(reqId, moduleId, msg));
};
var doSend = function(socket, topic, msg) {
socket.send(topic, msg);
}
/**
* broadcast msg to monitor
*
* @param {Object} record registered modules
* @param {String} moduleId module id/name
* @param {Object} msg message
* @api private
*/
var broadcastMonitors = function(records, moduleId, msg) {
msg = protocol.composeRequest(null, moduleId, msg);
if (records instanceof Array) {
for (var i = 0, l = records.length; i < l; i++) {
var socket = records[i].socket;
doSend(socket, 'monitor', msg);
}
} else {
for (var id in records) {
var socket = records[id].socket;
doSend(socket, 'monitor', msg);
}
}
};
var broadcastCommand = function(records, command, moduleId, msg) {
msg = protocol.composeCommand(null, command, moduleId, msg);
if (records instanceof Array) {
for (var i = 0, l = records.length; i < l; i++) {
var socket = records[i].socket;
doSend(socket, 'monitor', msg);
}
} else {
for (var id in records) {
var socket = records[id].socket;
doSend(socket, 'monitor', msg);
}
}
};
MasterAgent.prototype.doAuthUser = function(msg, socket, cb) {
if (!msg.id) {
// client should has a client id
return cb(new Error('client should has a client id'));
}
var self = this;
var username = msg.username;
if (!username) {
// client should auth with username
doSend(socket, 'register', {
code: protocol.PRO_FAIL,
msg: 'client should auth with username'
});
return cb(new Error('client should auth with username'));
}
var authUser = self.consoleService.authUser;
var env = self.consoleService.env;
authUser(msg, env, function(user) {
if (!user) {
// client should auth with username
doSend(socket, 'register', {
code: protocol.PRO_FAIL,
msg: 'client auth failed with username or password error'
});
return cb(new Error('client auth failed with username or password error'));
}
if (self.clients[msg.id]) {
doSend(socket, 'register', {
code: protocol.PRO_FAIL,
msg: 'id has been registered. id:' + msg.id
});
return cb(new Error('id has been registered. id:' + msg.id));
}
logger.info('client user : ' + username + ' login to master');
addConnection(self, msg.id, msg.type, null, user, socket);
doSend(socket, 'register', {
code: protocol.PRO_OK,
msg: 'ok'
});
cb();
});
};
MasterAgent.prototype.doAuthServer = function(msg, socket, cb) {
var self = this;
var authServer = self.consoleService.authServer;
var env = self.consoleService.env;
authServer(msg, env, function(status) {
if (status !== 'ok') {
doSend(socket, 'register', {
code: protocol.PRO_FAIL,
msg: 'server auth failed'
});
cb(new Error('server auth failed'));
return;
}
var record = addConnection(self, msg.id, msg.serverType, msg.pid, msg.info, socket);
doSend(socket, 'register', {
code: protocol.PRO_OK,
msg: 'ok'
});
msg.info = msg.info || {}
msg.info.pid = msg.pid;
self.emit('register', msg.info);
cb(null);
});
};
MasterAgent.prototype.doSend = doSend;
MasterAgent.prototype.sendToMonitor = sendToMonitor;
MasterAgent.prototype.addConnection = addConnection;
MasterAgent.prototype.removeConnection = removeConnection;
module.exports = MasterAgent;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MasterSocket');
var Constants = require('../util/constants');
var protocol = require('../util/protocol');
var MasterSocket = function() {
this.id = null;
this.type = null;
this.info = null;
this.agent = null;
this.socket = null;
this.username = null;
this.registered = false;
}
MasterSocket.prototype.onRegister = function(msg) {
if (!msg || !msg.type) {
return;
}
var self = this;
var serverId = msg.id;
var serverType = msg.type;
var socket = this.socket;
if (serverType == Constants.TYPE_CLIENT) {
// client connection not join the map
this.id = serverId;
this.type = serverType;
this.info = 'client';
this.agent.doAuthUser(msg, socket, function(err) {
if (err) {
return socket.disconnect();
}
self.username = msg.username;
self.registered = true;
});
return;
} // end of if(serverType === 'client')
if (serverType == Constants.TYPE_MONITOR) {
if (!serverId) {
return;
}
// if is a normal server
this.id = serverId;
this.type = msg.serverType;
this.info = msg.info;
this.agent.doAuthServer(msg, socket, function(err) {
if (err) {
return socket.disconnect();
}
self.registered = true;
});
this.repushQosMessage(serverId);
return;
} // end of if(serverType === 'monitor')
this.agent.doSend(socket, 'register', {
code: protocol.PRO_FAIL,
msg: 'unknown auth master type'
});
socket.disconnect();
}
MasterSocket.prototype.onMonitor = function(msg) {
var socket = this.socket;
if (!this.registered) {
// not register yet, ignore any message
// kick connections
socket.disconnect();
return;
}
var self = this;
var type = this.type;
if (type === Constants.TYPE_CLIENT) {
logger.error('invalid message from monitor, but current connect type is client.');
return;
}
msg = protocol.parse(msg);
var respId = msg.respId;
if (respId) {
// a response from monitor
var cb = self.agent.callbacks[respId];
if (!cb) {
logger.warn('unknown resp id:' + respId);
return;
}
var id = this.id;
if (self.agent.msgMap[id]) {
delete self.agent.msgMap[id][respId];
}
delete self.agent.callbacks[respId];
return cb(msg.error, msg.body);
}
// a request or a notify from monitor
self.agent.consoleService.execute(msg.moduleId, 'masterHandler', msg.body, function(err, res) {
if (protocol.isRequest(msg)) {
var resp = protocol.composeResponse(msg, err, res);
if (resp) {
self.agent.doSend(socket, 'monitor', resp);
}
} else {
//notify should not have a callback
logger.warn('notify should not have a callback.');
}
});
}
MasterSocket.prototype.onClient = function(msg) {
var socket = this.socket;
if (!this.registered) {
// not register yet, ignore any message
// kick connections
return socket.disconnect();
}
var type = this.type;
if (type !== Constants.TYPE_CLIENT) {
logger.error('invalid message to client, but current connect type is ' + type);
return;
}
msg = protocol.parse(msg);
var msgCommand = msg.command;
var msgModuleId = msg.moduleId;
var msgBody = msg.body;
var self = this;
if (msgCommand) {
// a command from client
self.agent.consoleService.command(msgCommand, msgModuleId, msgBody, function(err, res) {
if (protocol.isRequest(msg)) {
var resp = protocol.composeResponse(msg, err, res);
if (resp) {
self.agent.doSend(socket, 'client', resp);
}
} else {
//notify should not have a callback
logger.warn('notify should not have a callback.');
}
});
} else {
// a request or a notify from client
// and client should not have any response to master for master would not request anything from client
self.agent.consoleService.execute(msgModuleId, 'clientHandler', msgBody, function(err, res) {
if (protocol.isRequest(msg)) {
var resp = protocol.composeResponse(msg, err, res);
if (resp) {
self.agent.doSend(socket, 'client', resp);
}
} else {
//notify should not have a callback
logger.warn('notify should not have a callback.');
}
});
}
}
MasterSocket.prototype.onReconnect = function(msg, pid) {
// reconnect a new connection
if (!msg || !msg.type) {
return;
}
var serverId = msg.id;
if (!serverId) {
return;
}
var socket = this.socket;
// if is a normal server
if (this.agent.idMap[serverId]) {
// id has been registered
this.agent.doSend(socket, 'reconnect_ok', {
code: protocol.PRO_FAIL,
msg: 'id has been registered. id:' + serverId
});
return;
}
var msgServerType = msg.serverType;
var record = this.agent.addConnection(this.agent, serverId, msgServerType, msg.pid, msg.info, socket);
this.id = serverId;
this.type = msgServerType;
this.registered = true;
msg.info.pid = pid;
this.info = msg.info;
this.agent.doSend(socket, 'reconnect_ok', {
code: protocol.PRO_OK,
msg: 'ok'
});
this.agent.emit('reconnect', msg.info);
this.repushQosMessage(serverId);
}
MasterSocket.prototype.onDisconnect = function() {
var socket = this.socket;
if (socket) {
delete this.agent.sockets[socket.id];
}
var registered = this.registered;
if (!registered) {
return;
}
var id = this.id;
var type = this.type;
var info = this.info;
var username = this.username;
logger.debug('disconnect %s %s %j', id, type, info);
if (registered) {
this.agent.removeConnection(this.agent, id, type, info);
this.agent.emit('disconnect', id, type, info);
}
if (type === Constants.TYPE_CLIENT && registered) {
logger.info('client user ' + username + ' exit');
}
this.registered = false;
this.id = null;
this.type = null;
}
MasterSocket.prototype.repushQosMessage = function(serverId) {
var socket = this.socket;
// repush qos message
var qosMsgs = this.agent.msgMap[serverId];
if (!qosMsgs) {
return;
}
logger.debug('repush qos message %j', qosMsgs);
for (var reqId in qosMsgs) {
var qosMsg = qosMsgs[reqId];
var moduleId = qosMsg['moduleId'];
var tmsg = qosMsg['msg'];
this.agent.sendToMonitor(socket, reqId, moduleId, tmsg);
}
}
MasterSocket.prototype.onError = function(err) {
// logger.error('server %s error %s', this.id, err.stack);
// this.onDisconnect();
}
module.exports = MasterSocket;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| monitorLog.js | 21.15% | (11 / 52) | 0% | (0 / 10) | 0% | (0 / 9) | 21.15% | (11 / 52) | |
| nodeInfo.js | 33.33% | (10 / 30) | 0% | (0 / 14) | 0% | (0 / 6) | 33.33% | (10 / 30) | |
| profiler.js | 25% | (16 / 64) | 4.55% | (1 / 22) | 0% | (0 / 8) | 25% | (16 / 64) | |
| scripts.js | 24.62% | (16 / 65) | 0% | (0 / 16) | 0% | (0 / 12) | 24.62% | (16 / 65) | |
| systemInfo.js | 35.71% | (10 / 28) | 0% | (0 / 14) | 0% | (0 / 6) | 35.71% | (10 / 28) | |
| watchServer.js | 9.18% | (36 / 392) | 0% | (0 / 188) | 0% | (0 / 49) | 9.18% | (36 / 392) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 1 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule monitorLog
* Copyright(c) 2012 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var exec = require('child_process').exec;
var path = require('path');
var DEFAULT_INTERVAL = 5 * 60; // in second
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = 'monitorLog';
/**
* Initialize a new 'Module' with the given 'opts'
*
* @class Module
* @constructor
* @param {object} opts
* @api public
*/
var Module = function(opts) {
opts = opts || {};
this.root = opts.path;
this.interval = opts.interval || DEFAULT_INTERVAL;
};
/**
* collect monitor data from monitor
*
* @param {Object} agent monitorAgent object
* @param {Object} msg client message
* @param {Function} cb callback function
* @api public
*/
Module.prototype.monitorHandler = function(agent, msg, cb) {
if(!msg.logfile) {
cb(new Error('logfile should not be empty'));
return;
}
var serverId = agent.id;
fetchLogs(this.root, msg, function (data) {
cb(null, {serverId: serverId, body: data});
});
};
/**
* Handle client request
*
* @param {Object} agent masterAgent object
* @param {Object} msg client message
* @param {Function} cb callback function
* @api public
*/
Module.prototype.clientHandler = function(agent, msg, cb) {
agent.request(msg.serverId, module.exports.moduleId, msg, function(err, res) {
if(err) {
logger.error('fail to run log for ' + err.stack);
return;
}
cb(null, res);
});
};
//get the latest logs
var fetchLogs = function(root, msg, callback) {
var number = msg.number;
var logfile = msg.logfile;
var serverId = msg.serverId;
var filePath = path.join(root, getLogFileName(logfile, serverId));
var endLogs = [];
exec('tail -n ' + number + ' ' + filePath, function(error, output) {
var endOut = [];
output = output.replace(/^\s+|\s+$/g, "").split(/\s+/);
for(var i=5; i<output.length; i+=6) {
endOut.push(output[i]);
}
var endLength=endOut.length;
for(var j=0; j<endLength; j++) {
var map = {};
var json;
try{
json = JSON.parse(endOut[j]);
} catch(e) {
logger.error('the log cannot parsed to json, '+e);
continue;
}
map.time = json.time;
map.route = json.route || json.service;
map.serverId = serverId;
map.timeUsed = json.timeUsed;
map.params = endOut[j];
endLogs.push(map);
}
callback({logfile:logfile,dataArray:endLogs});
});
};
var getLogFileName = function(logfile, serverId) {
return logfile + '-' + serverId + '.log';
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule nodeInfo processInfo
* Copyright(c) 2012 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var monitor = require('pomelo-monitor');
var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var DEFAULT_INTERVAL = 5 * 60; // in second
var DEFAULT_DELAY = 10; // in second
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = 'nodeInfo';
var Module = function(opts) {
opts = opts || {};
this.type = opts.type || 'pull';
this.interval = opts.interval || DEFAULT_INTERVAL;
this.delay = opts.delay || DEFAULT_DELAY;
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
var serverId = agent.id;
var pid = process.pid;
var params = {
serverId: serverId,
pid: pid
};
monitor.psmonitor.getPsInfo(params, function (err, data) {
agent.notify(module.exports.moduleId, {serverId: agent.id, body: data});
});
};
Module.prototype.masterHandler = function(agent, msg, cb) {
if(!msg) {
agent.notifyAll(module.exports.moduleId);
return;
}
var body=msg.body;
var data = agent.get(module.exports.moduleId);
if(!data) {
data = {};
agent.set(module.exports.moduleId, data);
}
data[msg.serverId] = body;
};
Module.prototype.clientHandler = function(agent, msg, cb) {
cb(null, agent.get(module.exports.moduleId) || {});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var utils = require('../util/utils');
var profiler = null;
try {
profiler = require('v8-profiler');
} catch(e) {
}
var fs = require('fs');
var ProfileProxy = require('../util/profileProxy');
module.exports = function(opts) {
if (!profiler) {
return {};
} else {
return new Module(opts);
}
};
Eif (!profiler) {
module.exports.moduleError = 1;
}
module.exports.moduleId = 'profiler';
var Module = function(opts) {
if(opts && opts.isMaster) {
this.proxy = new ProfileProxy();
}
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
var type = msg.type, action = msg.action, uid = msg.uid, result = null;
if (type === 'CPU') {
if (action === 'start') {
profiler.startProfiling();
} else {
result = profiler.stopProfiling();
var res = {};
res.head = result.getTopDownRoot();
res.bottomUpHead = result.getBottomUpRoot();
res.msg = msg;
agent.notify(module.exports.moduleId, {clientId: msg.clientId, type: type, body: res});
}
} else {
var snapshot = profiler.takeSnapshot();
var appBase = path.dirname(require.main.filename);
var name = appBase + '/logs/' + utils.format(new Date()) + '.log';
var log = fs.createWriteStream(name, {'flags': 'a'});
var data;
snapshot.serialize({
onData: function (chunk, size) {
chunk = chunk + '';
data = {
method:'Profiler.addHeapSnapshotChunk',
params:{
uid: uid,
chunk: chunk
}
};
log.write(chunk);
agent.notify(module.exports.moduleId, {clientId: msg.clientId, type: type, body: data});
},
onEnd: function () {
agent.notify(module.exports.moduleId, {clientId: msg.clientId, type: type, body: {params: {uid: uid}}});
profiler.deleteAllSnapshots();
}
});
}
};
Module.prototype.masterHandler = function(agent, msg, cb) {
if(msg.type === 'CPU') {
this.proxy.stopCallBack(msg.body, msg.clientId, agent);
} else {
this.proxy.takeSnapCallBack(msg.body);
}
};
Module.prototype.clientHandler = function(agent, msg, cb) {
if(msg.action === 'list') {
list(agent, msg, cb);
return;
}
if(typeof msg === 'string') {
msg = JSON.parse(msg);
}
var id = msg.id;
var command = msg.method.split('.');
var method = command[1];
var params = msg.params;
var clientId = msg.clientId;
if (!this.proxy[method] || typeof this.proxy[method] !== 'function') {
return;
}
this.proxy[method](id, params, clientId, agent);
};
var list = function(agent, msg, cb) {
var servers = [];
var idMap = agent.idMap;
for(var sid in idMap){
servers.push(sid);
}
cb(null, servers);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule runScript
* Copyright(c) 2012 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var monitor = require('pomelo-monitor');
var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var monitor = require('pomelo-monitor');
var vm = require('vm');
var fs = require('fs');
var util = require('util');
var path = require('path');
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = "scripts";
var Module = function(opts) {
this.app = opts.app;
this.root = opts.path;
this.commands = {
'list': list,
'get': get,
'save': save,
'run': run
};
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
var context = {
app: this.app,
require: require,
os: require('os'),
fs: require('fs'),
process: process,
util: util
};
try {
vm.runInNewContext(msg.script, context);
var result = context.result;
if (!result) {
cb(null, "script result should be assigned to result value to script module context");
} else {
cb(null, result);
}
} catch (e) {
cb(null, e.toString());
}
//cb(null, vm.runInContext(msg.script, context));
};
Module.prototype.clientHandler = function(agent, msg, cb) {
var fun = this.commands[msg.command];
if (!fun || typeof fun !== 'function') {
cb('unknown command:' + msg.command);
return;
}
fun(this, agent, msg, cb);
};
/**
* List server id and scripts file name
*/
var list = function(scriptModule, agent, msg, cb) {
var servers = [];
var scripts = [];
var idMap = agent.idMap;
for (var sid in idMap) {
servers.push(sid);
}
fs.readdir(scriptModule.root, function(err, filenames) {
if (err) {
filenames = [];
}
for (var i = 0, l = filenames.length; i < l; i++) {
scripts.push(filenames[i]);
}
cb(null, {
servers: servers,
scripts: scripts
});
});
};
/**
* Get the content of the script file
*/
var get = function(scriptModule, agent, msg, cb) {
var filename = msg.filename;
if (!filename) {
cb('empty filename');
return;
}
fs.readFile(path.join(scriptModule.root, filename), 'utf-8', function(err, data) {
if (err) {
logger.error('fail to read script file:' + filename + ', ' + err.stack);
cb('fail to read script with name:' + filename);
}
cb(null, data);
});
};
/**
* Save a script file that posted from admin console
*/
var save = function(scriptModule, agent, msg, cb) {
var filepath = path.join(scriptModule.root, msg.filename);
fs.writeFile(filepath, msg.body, function(err) {
if (err) {
logger.error('fail to write script file:' + msg.filename + ', ' + err.stack);
cb('fail to write script file:' + msg.filename);
return;
}
cb();
});
};
/**
* Run the script on the specified server
*/
var run = function(scriptModule, agent, msg, cb) {
agent.request(msg.serverId, module.exports.moduleId, msg, function(err, res) {
if (err) {
logger.error('fail to run script for ' + err.stack);
return;
}
cb(null, res);
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule systemInfo
* Copyright(c) 2012 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var monitor = require('pomelo-monitor');
var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var DEFAULT_INTERVAL = 5 * 60; // in second
var DEFAULT_DELAY = 10; // in second
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = 'systemInfo';
var Module = function(opts) {
opts = opts || {};
this.type = opts.type || 'pull';
this.interval = opts.interval || DEFAULT_INTERVAL;
this.delay = opts.delay || DEFAULT_DELAY;
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
//collect data
monitor.sysmonitor.getSysInfo(function (err, data) {
agent.notify(module.exports.moduleId, {serverId: agent.id, body: data});
});
};
Module.prototype.masterHandler = function(agent, msg) {
if(!msg) {
agent.notifyAll(module.exports.moduleId);
return;
}
var body = msg.body;
var oneData = {
Time:body.iostat.date,hostname:body.hostname,serverId:msg.serverId,cpu_user:body.iostat.cpu.cpu_user,
cpu_nice:body.iostat.cpu.cpu_nice,cpu_system:body.iostat.cpu.cpu_system,cpu_iowait:body.iostat.cpu.cpu_iowait,
cpu_steal:body.iostat.cpu.cpu_steal,cpu_idle:body.iostat.cpu.cpu_idle,tps:body.iostat.disk.tps,
kb_read:body.iostat.disk.kb_read,kb_wrtn:body.iostat.disk.kb_wrtn,kb_read_per:body.iostat.disk.kb_read_per,
kb_wrtn_per:body.iostat.disk.kb_wrtn_per,totalmem:body.totalmem,freemem:body.freemem,'free/total':(body.freemem/body.totalmem),
m_1:body.loadavg[0],m_5:body.loadavg[1],m_15:body.loadavg[2]
};
var data = agent.get(module.exports.moduleId);
if(!data) {
data = {};
agent.set(module.exports.moduleId, data);
}
data[msg.serverId] = oneData;
};
Module.prototype.clientHandler = function(agent, msg, cb) {
cb(null, agent.get(module.exports.moduleId) || {});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule watchServer
* Copyright(c) 2013 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var logger = require('pomelo-logger').getLogger('pomelo-admin', __filename);
var countDownLatch = require('../util/countDownLatch');
var monitor = require('pomelo-monitor');
var utils = require('../util/utils');
var util = require('util');
var fs = require('fs');
var vm = require('vm');
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = 'watchServer';
var Module = function(opts) {
opts = opts || {};
this.app = opts.app;
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
var comd = msg['comd'];
var context = msg['context'];
var param = msg['param'];
var app = this.app;
var handle = 'monitor';
switch (comd) {
case 'servers':
showServers(handle, agent, comd, context, cb);
break;
case 'connections':
showConnections(handle, agent, app, comd, context, cb);
break;
case 'logins':
showLogins(handle, agent, app, comd, context, cb);
break;
case 'modules':
showModules(handle, agent, comd, context, cb);
break;
case 'status':
showStatus(handle, agent, comd, context, cb);
break;
case 'config':
showConfig(handle, agent, app, comd, context, param, cb);
break;
case 'proxy':
showProxy(handle, agent, app, comd, context, param, cb);
break;
case 'handler':
showHandler(handle, agent, app, comd, context, param, cb);
break;
case 'components':
showComponents(handle, agent, app, comd, context, param, cb);
break;
case 'settings':
showSettings(handle, agent, app, comd, context, param, cb);
break;
case 'cpu':
dumpCPU(handle, agent, comd, context, param, cb);
break;
case 'memory':
dumpMemory(handle, agent, comd, context, param, cb);
break;
case 'get':
getApp(handle, agent, app, comd, context, param, cb);
break;
case 'set':
setApp(handle, agent, app, comd, context, param, cb);
break;
case 'enable':
enableApp(handle, agent, app, comd, context, param, cb);
break;
case 'disable':
disableApp(handle, agent, app, comd, context, param, cb);
break;
case 'run':
runScript(handle, agent, app, comd, context, param, cb);
break;
default:
showError(handle, agent, comd, context, cb);
}
};
Module.prototype.clientHandler = function(agent, msg, cb) {
var comd = msg['comd'];
var context = msg['context'];
var param = msg['param'];
var app = this.app; // master app
if (!comd || !context) {
cb('lack of comd or context param');
return;
}
var handle = 'client';
switch (comd) {
case 'servers':
showServers(handle, agent, comd, context, cb);
break;
case 'connections':
showConnections(handle, agent, app, comd, context, cb);
break;
case 'logins':
showLogins(handle, agent, app, comd, context, cb);
break;
case 'modules':
showModules(handle, agent, comd, context, cb);
break;
case 'status':
showStatus(handle, agent, comd, context, cb);
break;
case 'config':
showConfig(handle, agent, app, comd, context, param, cb);
break;
case 'proxy':
showProxy(handle, agent, app, comd, context, param, cb);
break;
case 'handler':
showHandler(handle, agent, app, comd, context, param, cb);
break;
case 'components':
showComponents(handle, agent, app, comd, context, param, cb);
break;
case 'settings':
showSettings(handle, agent, app, comd, context, param, cb);
break;
case 'cpu':
dumpCPU(handle, agent, comd, context, param, cb);
break;
case 'memory':
dumpMemory(handle, agent, comd, context, param, cb);
break;
case 'get':
getApp(handle, agent, app, comd, context, param, cb);
break;
case 'set':
setApp(handle, agent, app, comd, context, param, cb);
break;
case 'enable':
enableApp(handle, agent, app, comd, context, param, cb);
break;
case 'disable':
disableApp(handle, agent, app, comd, context, param, cb);
break;
case 'run':
runScript(handle, agent, app, comd, context, param, cb);
break;
default:
showError(handle, agent, comd, context, cb);
}
};
function showServers(handle, agent, comd, context, cb) {
if (handle === 'client') {
var sid, record;
var serverInfo = {};
var count = utils.size(agent.idMap);
var latch = countDownLatch.createCountDownLatch(count, function() {
cb(null, {
msg: serverInfo
});
});
for (sid in agent.idMap) {
record = agent.idMap[sid];
agent.request(record.id, module.exports.moduleId, {
comd: comd,
context: context
}, function(msg) {
serverInfo[msg.serverId] = msg.body;
latch.done();
});
}
} else if (handle === 'monitor') {
var serverId = agent.id;
var serverType = agent.type;
var info = agent.info;
var pid = process.pid;
var heapUsed = (process.memoryUsage().heapUsed / (1000 * 1000)).toFixed(2);
var uptime = (process.uptime() / 60).toFixed(2);
cb({
serverId: serverId,
body: {
serverId: serverId,
serverType: serverType,
host: info['host'],
port: info['port'],
pid: pid,
heapUsed: heapUsed,
uptime: uptime
}
});
}
}
function showConnections(handle, agent, app, comd, context, cb) {
if (handle === 'client') {
if (context === 'all') {
var sid, record;
var serverInfo = {};
var count = 0;
for (var key in agent.idMap) {
if (agent.idMap[key].info.frontend === 'true') {
count++;
}
}
var latch = countDownLatch.createCountDownLatch(count, function() {
cb(null, {
msg: serverInfo
});
});
for (sid in agent.idMap) {
record = agent.idMap[sid];
if (record.info.frontend === 'true') {
agent.request(record.id, module.exports.moduleId, {
comd: comd,
context: context
}, function(msg) {
serverInfo[msg.serverId] = msg.body;
latch.done();
});
}
}
} else {
var record = agent.idMap[context];
if (!record) {
cb("the server " + context + " not exist");
}
if (record.info.frontend === 'true') {
agent.request(record.id, module.exports.moduleId, {
comd: comd,
context: context
}, function(msg) {
var serverInfo = {};
serverInfo[msg.serverId] = msg.body;
cb(null, {
msg: serverInfo
});
});
} else {
cb('\nthis command should be applied to frontend server\n');
}
}
} else if (handle === 'monitor') {
var connection = app.components.__connection__;
if (!connection) {
cb({
serverId: agent.id,
body: 'error'
});
return;
}
cb({
serverId: agent.id,
body: connection.getStatisticsInfo()
});
}
}
function showLogins(handle, agent, app, comd, context, cb) {
showConnections(handle, agent, app, comd, context, cb);
}
function showModules(handle, agent, comd, context, cb) {
var modules = agent.consoleService.modules;
var result = [];
for (var module in modules) {
result.push(module);
}
cb(null, {
msg: result
});
}
function showStatus(handle, agent, comd, context, cb) {
if (handle === 'client') {
agent.request(context, module.exports.moduleId, {
comd: comd,
context: context
}, function(err, msg) {
cb(null, {
msg: msg
});
});
} else if (handle === 'monitor') {
var serverId = agent.id;
var pid = process.pid;
var params = {
serverId: serverId,
pid: pid
};
monitor.psmonitor.getPsInfo(params, function(err, data) {
cb(null, {
serverId: agent.id,
body: data
})
});
}
}
function showConfig(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (param === 'master') {
cb(null, {
masterConfig: app.get('masterConfig') || 'no config to master in app.js',
masterInfo: app.get('master')
});
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var key = param + 'Config';
cb(null, clone(param, app.get(key)));
}
}
function showProxy(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
proxyCb(app, context, cb);
}
}
function showHandler(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
handlerCb(app, context, cb);
}
}
function showComponents(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var _components = app.components;
var res = {};
for (var key in _components) {
var name = getComponentName(key);
res[name] = clone(name, app.get(name + 'Config'))
}
cb(null, res);
}
}
function showSettings(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var _settings = app.settings;
var res = {};
for (var key in _settings) {
if (key.match(/^__\w+__$/) || key.match(/\w+Config$/)) {
continue;
}
if (!checkJSON(_settings[key])) {
res[key] = 'Object';
continue;
}
res[key] = _settings[key];
}
cb(null, res);
}
}
function dumpCPU(handle, agent, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(err, msg);
});
} else if (handle === 'monitor') {
var times = param['times'];
var filepath = param['filepath'];
var force = param['force'];
cb(null, 'cpu dump is unused in 1.0 of pomelo');
/**
if (!/\.cpuprofile$/.test(filepath)) {
filepath = filepath + '.cpuprofile';
}
if (!times || !/^[0-9]*[1-9][0-9]*$/.test(times)) {
cb('no times or times invalid error');
return;
}
checkFilePath(filepath, force, function(err) {
if (err) {
cb(err);
return;
}
//ndump.cpu(filepath, times);
cb(null, filepath + ' cpu dump ok');
});
*/
}
}
function dumpMemory(handle, agent, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(err, msg);
});
} else if (handle === 'monitor') {
var filepath = param['filepath'];
var force = param['force'];
if (!/\.heapsnapshot$/.test(filepath)) {
filepath = filepath + '.heapsnapshot';
}
checkFilePath(filepath, force, function(err) {
if (err) {
cb(err);
return;
}
var heapdump = null;
try {
heapdump = require('heapdump');
heapdump.writeSnapshot(filepath);
cb(null, filepath + ' memory dump ok')
} catch (e) {
cb('pomelo-admin require heapdump');
}
});
}
}
function getApp(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var res = app.get(param);
if (!checkJSON(res)) {
res = 'object';
}
cb(null, res || null);
}
}
function setApp(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var key = param['key'];
var value = param['value'];
app.set(key, value);
cb(null, 'set ' + key + ':' + value + ' ok');
}
}
function enableApp(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
app.enable(param);
cb(null, 'enable ' + param + ' ok');
}
}
function disableApp(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
app.disable(param);
cb(null, 'disable ' + param + ' ok');
}
}
function runScript(handle, agent, app, comd, context, param, cb) {
if (handle === 'client') {
if (context === 'all') {
cb('context error');
return;
}
agent.request(context, module.exports.moduleId, {
comd: comd,
param: param,
context: context
}, function(err, msg) {
cb(null, msg);
});
} else if (handle === 'monitor') {
var ctx = {
app: app,
result: null
};
try {
vm.runInNewContext('result = ' + param, ctx, 'myApp.vm');
cb(null, util.inspect(ctx.result));
} catch (e) {
cb(null, e.stack);
}
}
}
function showError(handle, agent, comd, context, cb) {
}
function clone(param, obj) {
var result = {};
var flag = 1;
for (var key in obj) {
if (typeof obj[key] === 'function' || typeof obj[key] === 'object') {
continue;
}
flag = 0;
result[key] = obj[key];
}
if (flag) {
// return 'no ' + param + 'Config info';
}
return result;
}
function checkFilePath(filepath, force, cb) {
if (!force && fs.existsSync(filepath)) {
cb('filepath file exist');
return;
}
fs.writeFile(filepath, 'test', function(err) {
if (err) {
cb('filepath invalid error');
return;
}
fs.unlinkSync(filepath);
cb(null);
})
}
function proxyCb(app, context, cb) {
var msg = {};
var __proxy__ = app.components.__proxy__;
if (__proxy__ && __proxy__.client && __proxy__.client.proxies.user) {
var proxies = __proxy__.client.proxies.user;
var server = app.getServerById(context);
if (!server) {
cb('no server with this id ' + context);
} else {
var type = server['serverType'];
var tmp = proxies[type];
msg[type] = {};
for (var _proxy in tmp) {
var r = tmp[_proxy];
msg[type][_proxy] = {};
for (var _rpc in r) {
if (typeof r[_rpc] === 'function') {
msg[type][_proxy][_rpc] = 'function';
}
}
}
cb(null, msg);
}
} else {
cb('no proxy loaded');
}
}
function handlerCb(app, context, cb) {
var msg = {};
var __server__ = app.components.__server__;
if (__server__ && __server__.server && __server__.server.handlerService.handlers) {
var handles = __server__.server.handlerService.handlers;
var server = app.getServerById(context);
if (!server) {
cb('no server with this id ' + context);
} else {
var type = server['serverType'];
var tmp = handles;
msg[type] = {};
for (var _p in tmp) {
var r = tmp[_p];
msg[type][_p] = {};
for (var _r in r) {
if (typeof r[_r] === 'function') {
msg[type][_p][_r] = 'function';
}
}
}
cb(null, msg);
}
} else {
cb('no handler loaded');
}
}
function getComponentName(c) {
var t = c.match(/^__(\w+)__$/);
if (t) {
t = t[1];
}
return t;
}
function checkJSON(obj) {
if (!obj) {
return true;
}
try {
JSON.stringify(obj);
} catch (e) {
return false;
}
return true;
}
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| monitorAgent.js | 19.81% | (21 / 106) | 0% | (0 / 34) | 0% | (0 / 19) | 19.81% | (21 / 106) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MonitorAgent');
var MqttClient = require('../protocol/mqtt/mqttClient');
var EventEmitter = require('events').EventEmitter;
var protocol = require('../util/protocol');
var utils = require('../util/utils');
var Util = require('util');
var ST_INITED = 1;
var ST_CONNECTED = 2;
var ST_REGISTERED = 3;
var ST_CLOSED = 4;
var STATUS_INTERVAL = 5 * 1000; // 60 seconds
/**
* MonitorAgent Constructor
*
* @class MasterAgent
* @constructor
* @param {Object} opts construct parameter
* opts.consoleService {Object} consoleService
* opts.id {String} server id
* opts.type {String} server type, 'master', 'connector', etc.
* opts.info {Object} more server info for current server, {id, serverType, host, port}
* @api public
*/
var MonitorAgent = function(opts) {
EventEmitter.call(this);
this.reqId = 1;
this.opts = opts;
this.id = opts.id;
this.socket = null;
this.callbacks = {};
this.type = opts.type;
this.info = opts.info;
this.state = ST_INITED;
this.consoleService = opts.consoleService;
};
Util.inherits(MonitorAgent, EventEmitter);
/**
* register and connect to master server
*
* @param {String} port
* @param {String} host
* @param {Function} cb callback function
* @api public
*/
MonitorAgent.prototype.connect = function(port, host, cb) {
if (this.state > ST_INITED) {
logger.error('monitor client has connected or closed.');
return;
}
cb = cb || function() {}
this.socket = new MqttClient(this.opts);
this.socket.connect(host, port);
// this.socket = sclient.connect(host + ':' + port, {
// 'force new connection': true,
// 'reconnect': true,
// 'max reconnection attempts': 20
// });
var self = this;
this.socket.on('register', function(msg) {
if (msg && msg.code === protocol.PRO_OK) {
self.state = ST_REGISTERED;
cb();
} else {
self.emit('close');
logger.error('server %j %j register master failed', self.id, self.type);
}
});
this.socket.on('monitor', function(msg) {
if (self.state !== ST_REGISTERED) {
return;
}
msg = protocol.parse(msg);
if (msg.command) {
// a command from master
self.consoleService.command(msg.command, msg.moduleId, msg.body, function(err, res) {
//notify should not have a callback
});
} else {
var respId = msg.respId;
if (respId) {
// a response from monitor
var respCb = self.callbacks[respId];
if (!respCb) {
logger.warn('unknown resp id:' + respId);
return;
}
delete self.callbacks[respId];
respCb(msg.error, msg.body);
return;
}
// request from master
self.consoleService.execute(msg.moduleId, 'monitorHandler', msg.body, function(err, res) {
if (protocol.isRequest(msg)) {
var resp = protocol.composeResponse(msg, err, res);
if (resp) {
self.doSend('monitor', resp);
}
} else {
//notify should not have a callback
logger.error('notify should not have a callback.');
}
});
}
});
this.socket.on('connect', function() {
if (self.state > ST_INITED) {
//ignore reconnect
return;
}
self.state = ST_CONNECTED;
var req = {
id: self.id,
type: 'monitor',
serverType: self.type,
pid: process.pid,
info: self.info
};
var authServer = self.consoleService.authServer;
var env = self.consoleService.env;
authServer(req, env, function(token) {
req['token'] = token;
self.doSend('register', req);
});
});
this.socket.on('error', function(err) {
if (self.state < ST_CONNECTED) {
// error occurs during connecting stage
cb(err);
} else {
self.emit('error', err);
}
});
this.socket.on('disconnect', function(reason) {
self.state = ST_CLOSED;
self.emit('close');
});
this.socket.on('reconnect', function() {
self.state = ST_CONNECTED;
var req = {
id: self.id,
type: 'monitor',
info: self.info,
pid: process.pid,
serverType: self.type
};
self.doSend('reconnect', req);
});
this.socket.on('reconnect_ok', function(msg) {
if (msg && msg.code === protocol.PRO_OK) {
self.state = ST_REGISTERED;
}
});
};
/**
* close monitor agent
*
* @api public
*/
MonitorAgent.prototype.close = function() {
if (this.state >= ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.socket.disconnect();
};
/**
* set module
*
* @param {String} moduleId module id/name
* @param {Object} value module object
* @api public
*/
MonitorAgent.prototype.set = function(moduleId, value) {
this.consoleService.set(moduleId, value);
};
/**
* get module
*
* @param {String} moduleId module id/name
* @api public
*/
MonitorAgent.prototype.get = function(moduleId) {
return this.consoleService.get(moduleId);
};
/**
* notify master server without callback
*
* @param {String} moduleId module id/name
* @param {Object} msg message
* @api public
*/
MonitorAgent.prototype.notify = function(moduleId, msg) {
if (this.state !== ST_REGISTERED) {
logger.error('agent can not notify now, state:' + this.state);
return;
}
this.doSend('monitor', protocol.composeRequest(null, moduleId, msg));
// this.socket.emit('monitor', protocol.composeRequest(null, moduleId, msg));
};
MonitorAgent.prototype.request = function(moduleId, msg, cb) {
if (this.state !== ST_REGISTERED) {
logger.error('agent can not request now, state:' + this.state);
return;
}
var reqId = this.reqId++;
this.callbacks[reqId] = cb;
this.doSend('monitor', protocol.composeRequest(reqId, moduleId, msg));
// this.socket.emit('monitor', protocol.composeRequest(reqId, moduleId, msg));
};
MonitorAgent.prototype.doSend = function(topic, msg) {
this.socket.send(topic, msg);
}
module.exports = MonitorAgent;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| mqttClient.js | 13.57% | (19 / 140) | 0% | (0 / 38) | 0% | (0 / 22) | 13.57% | (19 / 140) | |
| mqttServer.js | 25.53% | (12 / 47) | 0% | (0 / 4) | 0% | (0 / 10) | 25.53% | (12 / 47) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MqttClient');
var EventEmitter = require('events').EventEmitter;
var constants = require('../../util/constants');
var MqttCon = require('mqtt-connection');
var Util = require('util');
var net = require('net');
var MqttClient = function(opts) {
EventEmitter.call(this);
this.clientId = 'MQTT_ADMIN_' + Date.now();
this.id = opts.id;
this.requests = {};
this.connectedTimes = 1;
this.host = null;
this.port = null;
this.socket = null;
this.lastPing = -1;
this.lastPong = -1;
this.closed = false;
this.timeoutId = null;
this.connected = false;
this.reconnectId = null;
this.timeoutFlag = false;
this.keepaliveTimer = null;
this.reconnectDelay = 0;
this.reconnectDelayMax = opts.reconnectDelayMax || constants.DEFAULT_PARAM.RECONNECT_DELAY_MAX;
this.timeout = opts.timeout || constants.DEFAULT_PARAM.TIMEOUT;
this.keepalive = opts.keepalive || constants.DEFAULT_PARAM.KEEPALIVE;
}
Util.inherits(MqttClient, EventEmitter);
MqttClient.prototype.connect = function(host, port, cb) {
cb = cb || function() {}
if (this.connected) {
return cb(new Error('MqttClient has already connected.'));
}
if (host) {
this.host = host;
} else {
host = this.host;
}
if (port) {
this.port = port;
} else {
port = this.port;
}
var self = this;
this.closed = false;
var stream = net.createConnection(this.port, this.host);
this.socket = MqttCon(stream);
// logger.info('try to connect %s %s', this.host, this.port);
this.socket.connect({
clientId: this.clientId
});
this.addTimeout();
this.socket.on('connack', function() {
if (self.connected) {
return;
}
self.connected = true;
self.setupKeepAlive();
if (self.connectedTimes++ == 1) {
self.emit('connect');
cb();
} else {
self.emit('reconnect');
}
});
this.socket.on('publish', function(pkg) {
var topic = pkg.topic;
var msg = pkg.payload.toString();
msg = JSON.parse(msg);
// logger.debug('[MqttClient] publish %s %j', topic, msg);
self.emit(topic, msg);
});
this.socket.on('close', function() {
logger.error('mqtt socket is close, remote server host: %s, port: %s', host, port);
self.onSocketClose();
});
this.socket.on('error', function(err) {
logger.error('mqtt socket is error, remote server host: %s, port: %s', host, port);
// self.emit('error', new Error('[MqttClient] socket is error, remote server ' + host + ':' + port));
self.onSocketClose();
});
this.socket.on('pingresp', function() {
self.lastPong = Date.now();
});
this.socket.on('disconnect', function() {
logger.error('mqtt socket is disconnect, remote server host: %s, port: %s', host, port);
self.emit('disconnect', self.id);
self.onSocketClose();
});
this.socket.on('timeout', function(reconnectFlag) {
if (reconnectFlag) {
self.reconnect();
} else {
self.exit();
}
})
}
MqttClient.prototype.send = function(topic, msg) {
// console.log('MqttClient send %s %j ~~~', topic, msg);
this.socket.publish({
topic: topic,
payload: JSON.stringify(msg)
})
}
MqttClient.prototype.onSocketClose = function() {
// console.log('onSocketClose ' + this.closed);
if (this.closed) {
return;
}
clearInterval(this.keepaliveTimer);
clearTimeout(this.timeoutId);
this.keepaliveTimer = null;
this.lastPing = -1;
this.lastPong = -1;
this.connected = false;
this.closed = true;
delete this.socket;
this.socket = null;
if (this.connectedTimes > 1) {
this.reconnect();
} else {
this.exit();
}
}
MqttClient.prototype.addTimeout = function(reconnectFlag) {
var self = this;
if (this.timeoutFlag) {
return;
}
this.timeoutFlag = true;
this.timeoutId = setTimeout(function() {
self.timeoutFlag = false;
logger.error('mqtt client connect %s:%d timeout %d s', self.host, self.port, self.timeout / 1000);
self.socket.emit('timeout', reconnectFlag);
}, self.timeout);
}
MqttClient.prototype.reconnect = function() {
var delay = this.reconnectDelay * 2 || constants.DEFAULT_PARAM.RECONNECT_DELAY;
if (delay > this.reconnectDelayMax) {
delay = this.reconnectDelayMax;
}
this.reconnectDelay = delay;
var self = this;
// logger.debug('[MqttClient] reconnect %d ...', delay);
this.reconnectId = setTimeout(function() {
logger.info('reconnect delay %d s', delay / 1000);
self.addTimeout(true);
self.connect();
}, delay);
}
MqttClient.prototype.setupKeepAlive = function() {
clearTimeout(this.reconnectId);
clearTimeout(this.timeoutId);
var self = this;
this.keepaliveTimer = setInterval(function() {
self.checkKeepAlive();
}, this.keepalive);
}
MqttClient.prototype.checkKeepAlive = function() {
if (this.closed) {
return;
}
var now = Date.now();
var KEEP_ALIVE_TIMEOUT = this.keepalive * 2;
if (this.lastPing > 0) {
if (this.lastPong < this.lastPing) {
if (now - this.lastPing > KEEP_ALIVE_TIMEOUT) {
logger.error('mqtt rpc client checkKeepAlive error timeout for %d', KEEP_ALIVE_TIMEOUT);
this.close();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
}
MqttClient.prototype.disconnect = function() {
this.close();
}
MqttClient.prototype.close = function() {
this.connected = false;
this.closed = true;
this.socket.disconnect();
}
MqttClient.prototype.exit = function() {
logger.info('exit ...');
process.exit(0);
}
module.exports = MqttClient;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-admin', 'MqttServer');
var EventEmitter = require('events').EventEmitter;
var MqttCon = require('mqtt-connection');
var Util = require('util');
var net = require('net');
var curId = 1;
var MqttServer = function(opts, cb) {
EventEmitter.call(this);
this.inited = false;
this.closed = true;
};
Util.inherits(MqttServer, EventEmitter);
MqttServer.prototype.listen = function(port) {
//check status
if (this.inited) {
this.cb(new Error('already inited.'));
return;
}
this.inited = true;
var self = this;
this.server = new net.Server();
this.server.listen(port);
logger.info('[MqttServer] listen on %d', port);
this.server.on('listening', this.emit.bind(this, 'listening'));
this.server.on('error', function(err) {
// logger.error('mqtt server is error: %j', err.stack);
self.emit('error', err);
});
this.server.on('connection', function(stream) {
var socket = MqttCon(stream);
socket['id'] = curId++;
socket.on('connect', function(pkg) {
socket.connack({
returnCode: 0
});
});
socket.on('publish', function(pkg) {
var topic = pkg.topic;
var msg = pkg.payload.toString();
msg = JSON.parse(msg);
// logger.debug('[MqttServer] publish %s %j', topic, msg);
socket.emit(topic, msg);
});
socket.on('pingreq', function() {
socket.pingresp();
});
socket.send = function(topic, msg) {
socket.publish({
topic: topic,
payload: JSON.stringify(msg)
});
};
self.emit('connection', socket);
});
};
MqttServer.prototype.send = function(topic, msg) {
this.socket.publish({
topic: topic,
payload: msg
});
}
MqttServer.prototype.close = function() {
if (this.closed) {
return;
}
this.closed = true;
this.server.close();
this.emit('closed');
};
module.exports = MqttServer;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| constants.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| countDownLatch.js | 25% | (4 / 16) | 0% | (0 / 10) | 0% | (0 / 3) | 25% | (4 / 16) | |
| profileProxy.js | 23.08% | (21 / 91) | 0% | (0 / 22) | 0% | (0 / 17) | 23.08% | (21 / 91) | |
| protocol.js | 32% | (8 / 25) | 0% | (0 / 12) | 0% | (0 / 6) | 32% | (8 / 25) | |
| utils.js | 11.4% | (13 / 114) | 0% | (0 / 58) | 0% | (0 / 8) | 11.4% | (13 / 114) |
| 1 2 3 4 5 6 7 8 9 10 11 | 1 | module.exports = {
DEFAULT_PARAM: {
KEEPALIVE: 60 * 1000,
TIMEOUT: 5 * 1000,
RECONNECT_DELAY: 1 * 1000,
RECONNECT_DELAY_MAX: 60 * 1000
},
TYPE_CLIENT: 'client',
TYPE_MONITOR: 'monitor'
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 1 1 1 1 | var exp = module.exports; /** * Count down to zero and invoke cb finally. */ var CountDownLatch = function(count, cb) { this.count = count; this.cb = cb; }; /** * Call when a task finish to count down. * * @api public */ CountDownLatch.prototype.done = function() { if(this.count <= 0) { throw new Error('illegal state.'); } this.count--; if (this.count === 0) { this.cb(); } }; /** * create a count down latch * * @api public */ exp.createCountDownLatch = function(count, cb) { if(!count || count <= 0) { throw new Error('count should be positive.'); } if(typeof cb !== 'function') { throw new Error('cb should be a function.'); } return new CountDownLatch(count, cb); }; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var fs = require('fs');
var HeapProfileType = 'HEAP';
var CPUProfileType = 'CPU';
var Proxy = function(){
this.profiles = {
HEAP: {},
CPU: {}
};
this.isProfilingCPU = false;
};
module.exports = Proxy;
var pro = Proxy.prototype;
pro.enable = function(id, params, clientId, agent) {
this.sendResult(id,{
result : true
}, clientId, agent);
};
pro.causesRecompilation = function(id, params, clientId, agent) {
this.sendResult(id,{
result: false
}, clientId, agent);
};
pro.isSampling = function(id, params, clientId, agent) {
this.sendResult(id,{
result: true
}, clientId, agent);
};
pro.hasHeapProfiler = function(id, params, clientId, agent) {
this.sendResult(id,{
result: true
}, clientId, agent);
};
pro.getProfileHeaders = function(id, params, clientId, agent) {
var headers = [];
for (var type in this.profiles) {
for (var profileId in this.profiles[type]) {
var profile = this.profiles[type][profileId];
headers.push({
title: profile.title,
uid: profile.uid,
typeId: type
});
}
}
this.sendResult(id, {
headers: headers
}, clientId, agent);
};
pro.takeHeapSnapshot = function(id, params, clientId, agent) {
var uid = params.uid;
agent.notifyById(uid, 'profiler', {type: 'heap', action: 'start', uid: uid, clientId: clientId});
this.sendEvent({
method: 'Profiler.addProfileHeader',
params: {header: {title: uid, uid: uid, typeId: HeapProfileType}}
}, clientId, agent);
this.sendResult(id, {}, clientId, agent);
};
pro.takeSnapCallBack = function (data) {
var uid = data.params.uid || 0;
var snapShot = this.profiles[HeapProfileType][uid];
if (!snapShot || snapShot.finish) {
snapShot = {};
snapShot.data = [];
snapShot.finish = false;
snapShot.uid = uid;
snapShot.title = uid;
}
if (data.method === 'Profiler.addHeapSnapshotChunk') {
var chunk = data.params.chunk;
snapShot.data.push(chunk);
} else {
snapShot.finish = true;
}
this.profiles[HeapProfileType][uid] = snapShot;
};
pro.getProfile = function(id, params, clientId, agent) {
var profile = this.profiles[params.type][params.uid];
var self = this;
if (!profile || !profile.finish) {
var timerId = setInterval(function() {
profile = self.profiles[params.type][params.uid];
if (!!profile) {
clearInterval(timerId);
self.asyncGet(id, params, profile, clientId, agent);
}
}, 5000);
} else {
this.asyncGet(id,params, profile, clientId, agent);
}
};
pro.asyncGet = function(id, params, snapshot, clientId, agent) {
var uid = params.uid;
if (params.type === HeapProfileType) {
for (var index in snapshot.data) {
var chunk = snapshot.data[index];
this.sendEvent({method: 'Profiler.addHeapSnapshotChunk', params: {uid: uid, chunk: chunk}}, clientId, agent);
}
this.sendEvent({method: 'Profiler.finishHeapSnapshot', params: {uid: uid}}, clientId, agent);
this.sendResult(id, {profile: {title: snapshot.title, uid: uid, typeId: HeapProfileType}}, clientId, agent);
} else if (params.type === CPUProfileType) {
this.sendResult(id,{
profile: {
title: snapshot.title,
uid: uid,
typeId: CPUProfileType,
head: snapshot.data.head,
bottomUpHead: snapshot.data.bottomUpHead
}
}, clientId, agent);
}
};
pro.clearProfiles = function(id, params) {
this.profiles.HEAP = {};
this.profiles.CPU = {};
//profiler.deleteAllSnapshots();
//profiler.deleteAllProfiles();
};
pro.sendResult = function(id, res, clientId, agent){
agent.notifyClient(clientId, 'profiler', JSON.stringify({id: id, result: res}));
};
pro.sendEvent = function(res, clientId, agent){
agent.notifyClient(clientId, 'profiler', JSON.stringify(res));
};
pro.start = function(id, params, clientId, agent) {
var uid = params.uid;
agent.notifyById(uid, 'profiler', {type: 'CPU', action: 'start', uid: uid, clientId: clientId});
this.sendEvent({method: 'Profiler.setRecordingProfile', params: {isProfiling: true}}, clientId, agent);
this.sendResult(id, {}, clientId, agent);
};
pro.stop = function(id, params, clientId, agent) {
var uid = params.uid;
agent.notifyById(uid, 'profiler', {type: 'CPU', action: 'stop', uid: uid, clientId: clientId});
this.sendResult(id, {}, clientId, agent);
};
pro.stopCallBack = function(res, clientId, agent) {
var uid = res.msg.uid;
var profiler = this.profiles[CPUProfileType][uid];
if (!profiler || profiler.finish){
profiler = {};
profiler.data = null;
profiler.finish = true;
profiler.typeId = CPUProfileType;
profiler.uid = uid;
profiler.title = uid;
}
profiler.data = res;
this.profiles[CPUProfileType][uid] = profiler;
this.sendEvent({
method: 'Profiler.addProfileHeader',
params: {header: {title: profiler.title, uid: uid, typeId: CPUProfileType}}
}, clientId, agent);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 1 1 1 1 1 1 1 1 | exports.composeRequest = function(id, moduleId, body) { if (id) { // request message return JSON.stringify({ reqId: id, moduleId: moduleId, body: body }); } else { // notify message return { moduleId: moduleId, body: body }; } }; exports.composeResponse = function(req, err, res) { if (req.reqId) { // request only return JSON.stringify({ respId: req.reqId, error: cloneError(err), body: res }); } // invalid message(notify dose not need response) return null; }; exports.composeCommand = function(id, command, moduleId, body) { if (id) { // command message return JSON.stringify({ reqId: id, command: command, moduleId: moduleId, body: body }); } else { return JSON.stringify({ command: command, moduleId: moduleId, body: body }); } } exports.parse = function(msg) { if (typeof msg === 'string') { return JSON.parse(msg); } return msg; }; exports.isRequest = function(msg) { return (msg && msg.reqId); }; var cloneError = function(origin) { // copy the stack infos for Error instance json result is empty if (!(origin instanceof Error)) { return origin; } var res = { message: origin.message, stack: origin.stack }; return res; }; exports.PRO_OK = 1; exports.PRO_FAIL = -1; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | 1 1 1 1 1 1 1 1 1 1 1 1 1 | var crypto = require('crypto');
var util = require('util');
var path = require('path');
var fs = require('fs');
var utils = module.exports;
/**
* Check and invoke callback
*/
utils.invokeCallback = function(cb) {
if (!!cb && typeof cb === 'function') {
cb.apply(null, Array.prototype.slice.call(arguments, 1));
}
};
/*
* Date format
*/
utils.format = function(date, format) {
format = format || 'MM-dd-hhmm';
var o = {
"M+": date.getMonth() + 1, //month
"d+": date.getDate(), //day
"h+": date.getHours(), //hour
"m+": date.getMinutes(), //minute
"s+": date.getSeconds(), //second
"q+": Math.floor((date.getMonth() + 3) / 3), //quarter
"S": date.getMilliseconds() //millisecond
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1,
RegExp.$1.length === 1 ? o[k] :
("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
};
utils.compareServer = function(server1, server2) {
return (server1['host'] === server2['host']) &&
(server1['port'] === server2['port']);
}
/**
* Get the count of elements of object
*/
utils.size = function(obj, type) {
var count = 0;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof obj[i] !== 'function') {
if (!type) {
count++;
continue;
}
if (type && type === obj[i]['type']) {
count++;
}
}
}
return count;
};
utils.md5 = function(str) {
var md5sum = crypto.createHash('md5');
md5sum.update(str);
str = md5sum.digest('hex');
return str;
}
utils.defaultAuthUser = function(msg, env, cb) {
var adminUser = null;
var appBase = path.dirname(require.main.filename);
var adminUserPath = path.join(appBase, '/config/adminUser.json');
var presentPath = path.join(appBase, 'config', env, 'adminUser.json');
if (fs.existsSync(adminUserPath)) {
adminUser = require(adminUserPath);
} else if (fs.existsSync(presentPath)) {
adminUser = require(presentPath);
} else {
cb(null);
return;
}
var username = msg['username'];
var password = msg['password'];
var md5 = msg['md5'];
var len = adminUser.length;
if (md5) {
for (var i = 0; i < len; i++) {
var user = adminUser[i];
var p = "";
if (user['username'] === username) {
p = utils.md5(user['password']);
if (password === p) {
cb(user);
return;
}
}
}
} else {
for (var i = 0; i < len; i++) {
var user = adminUser[i];
if (user['username'] === username && user['password'] === password) {
cb(user);
return;
}
}
}
cb(null);
}
utils.defaultAuthServerMaster = function(msg, env, cb) {
var id = msg['id'];
var type = msg['serverType'];
var token = msg['token'];
if (type === 'master') {
cb('ok');
return;
}
var servers = null;
var appBase = path.dirname(require.main.filename);
var serverPath = path.join(appBase, '/config/adminServer.json');
var presentPath = null;
if (env) {
presentPath = path.join(appBase, 'config', env, 'adminServer.json');
}
if (fs.existsSync(serverPath)) {
servers = require(serverPath);
} else if (fs.existsSync(presentPath)) {
servers = require(presentPath);
} else {
cb('ok');
return;
}
var len = servers.length;
for (var i = 0; i < len; i++) {
var server = servers[i];
if (server['type'] === type && server['token'] === token) {
cb('ok');
return;
}
}
cb('bad');
return;
}
utils.defaultAuthServerMonitor = function(msg, env, cb) {
var id = msg['id'];
var type = msg['serverType'];
var servers = null;
var appBase = path.dirname(require.main.filename);
var serverPath = path.join(appBase, '/config/adminServer.json');
var presentPath = null;
if (env) {
presentPath = path.join(appBase, 'config', env, 'adminServer.json');
}
if (fs.existsSync(serverPath)) {
servers = require(serverPath);
} else if (fs.existsSync(presentPath)) {
servers = require(presentPath);
} else {
cb('ok');
return;
}
var len = servers.length;
for (var i = 0; i < len; i++) {
var server = servers[i];
if (server['type'] === type) {
cb(server['token']);
return;
}
}
cb(null);
return;
}
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| cronTrigger.js | 11.28% | (22 / 195) | 0% | (0 / 96) | 0% | (0 / 13) | 11.28% | (22 / 195) | |
| job.js | 43.24% | (16 / 37) | 0% | (0 / 8) | 0% | (0 / 5) | 43.24% | (16 / 37) | |
| priorityQueue.js | 25.93% | (14 / 54) | 5.88% | (1 / 17) | 37.5% | (3 / 8) | 25.93% | (14 / 54) | |
| schedule.js | 26.87% | (18 / 67) | 0% | (0 / 26) | 0% | (0 / 7) | 27.27% | (18 / 66) | |
| simpleTrigger.js | 35% | (7 / 20) | 0% | (0 / 15) | 0% | (0 / 4) | 35% | (7 / 20) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* This is the trigger used to decode the cronTimer and calculate the next excution time of the cron Trigger.
*/
var logger = require('log4js').getLogger(__filename);
var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;
var Limit = [[0,59],[0,59],[0,24],[1,31],[0,11],[0,6]];
/**
* The constructor of the CronTrigger
* @param trigger The trigger str used to build the cronTrigger instance
*/
var CronTrigger = function(trigger, job){
this.trigger = this.decodeTrigger(trigger);
this.nextTime = this.nextExcuteTime(Date.now());
this.job = job;
};
var pro = CronTrigger.prototype;
/**
* Get the current excuteTime of trigger
*/
pro.excuteTime = function(){
return this.nextTime;
};
/**
* Caculate the next valid cronTime after the given time
* @param The given time point
* @return The nearest valid time after the given time point
*/
pro.nextExcuteTime = function(time){
//add 1s to the time so it must be the next time
time = !!time?time:this.nextTime;
time += 1000;
var cronTrigger = this.trigger;
var date = new Date(time);
date.setMilliseconds(0);
outmost:
while(true){
if(date.getFullYear() > 2999){
logger.error("Can't compute the next time, exceed the limit");
return null;
}
if(!timeMatch(date.getMonth(), cronTrigger[MONTH])){
var nextMonth = nextCronTime(date.getMonth(), cronTrigger[MONTH]);
if(nextMonth == null)
return null;
if(nextMonth <= date.getMonth()){
date.setYear(date.getFullYear() + 1);
date.setMonth(0);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setMonth(nextMonth);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getDate(), cronTrigger[DOM]) || !timeMatch(date.getDay(), cronTrigger[DOW])){
var domLimit = getDomLimit(date.getFullYear(), date.getMonth());
do{
var nextDom = nextCronTime(date.getDate(), cronTrigger[DOM]);
if(nextDom == null)
return null;
//If the date is in the next month, add month
if(nextDom <= date.getDate() || nextDom > domLimit){
date.setMonth(date.getMonth() + 1);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue outmost;
}
date.setDate(nextDom);
}while(!timeMatch(date.getDay(), cronTrigger[DOW]));
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getHours(), cronTrigger[HOUR])){
var nextHour = nextCronTime(date.getHours(), cronTrigger[HOUR]);
if(nextHour <= date.getHours()){
date.setDate(date.getDate() + 1);
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getMinutes(), cronTrigger[MIN])){
var nextMinute = nextCronTime(date.getMinutes(), cronTrigger[MIN]);
if(nextMinute <= date.getMinutes()){
date.setHours(date.getHours() + 1);
date.setMinutes(nextMinute);
date.setSeconds(0);
continue;
}
date.setMinutes(nextMinute);
date.setSeconds(0);
}
if(!timeMatch(date.getSeconds(), cronTrigger[SECOND])){
var nextSecond = nextCronTime(date.getSeconds(), cronTrigger[SECOND]);
if(nextSecond <= date.getSeconds()){
date.setMinutes(date.getMinutes() + 1);
date.setSeconds(nextSecond);
continue;
}
date.setSeconds(nextSecond);
}
break;
}
this.nextTime = date.getTime();
return this.nextTime;
};
/**
* return the next match time of the given value
* @param value The time value
* @param cronTime The cronTime need to match
* @return The match value or null if unmatch(it offten means an error occur).
*/
function nextCronTime(value, cronTime){
value += 1;
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return value;
else
return cronTime;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value <= cronTime[0] || value > cronTime[cronTime.length -1])
return cronTime[0];
for(var i = 0; i < cronTime.length; i++)
if(value <= cronTime[i])
return cronTime[i];
}
logger.warn('Compute next Time error! value :' + value + ' cronTime : ' + cronTime);
return null;
}
/**
* Match the given value to the cronTime
* @param value The given value
* @param cronTime The cronTime
* @return The match result
*/
function timeMatch(value, cronTime){
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return true;
if(value == cronTime)
return true;
return false;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value < cronTime[0] || value > cronTime[cronTime.length -1])
return false;
for(var i = 0; i < cronTime.length; i++)
if(value == cronTime[i])
return true;
return false;
}
return null;
}
/**
* Decude the cronTrigger string to arrays
* @param cronTimeStr The cronTimeStr need to decode, like "0 12 * * * 3"
* @return The array to represent the cronTimer
*/
pro.decodeTrigger = function(cronTimeStr){
var cronTimes = cronTimeStr.split(/\s+/);
if(cronTimes.length != 6){
console.log('error');
return null;
}
for(var i = 0; i < cronTimes.length; i++){
cronTimes[i] = (this.decodeTimeStr(cronTimes[i], i));
if(!checkNum(cronTimes[i], Limit[i][0], Limit[i][1])){
logger.error('Decode crontime error, value exceed limit!' +
JSON.stringify({cronTime: cronTimes[i], limit:Limit[i]}));
return null;
}
}
return cronTimes;
}
/**
* Decode the cron Time string
* @param timeStr The cron time string, like: 1,2 or 1-3
* @return A sorted array, like [1,2,3]
*/
pro.decodeTimeStr = function(timeStr, type){
var result = {};
var arr = [];
if(timeStr=='*'){
return -1;
}else if(timeStr.search(',')>0){
var timeArr = timeStr.split(',');
for(var i = 0; i < timeArr.length; i++){
var time = timeArr[i];
if(time.match(/^\d+-\d+$/)){
decodeRangeTime(result, time);
}else if(time.match(/^\d+\/\d+/)){
decodePeriodTime(result, time, type);
}else if(!isNaN(time)){
var num = Number(time);
result[num] = num;
}else
return null;
}
}else if(timeStr.match(/^\d+-\d+$/)){
decodeRangeTime(result, timeStr);
}else if(timeStr.match(/^\d+\/\d+/)){
decodePeriodTime(result, timeStr, type);
}else if(!isNaN(timeStr)){
var num = Number(timeStr);
result[num] = num;
}else{
return null;
}
for(var key in result){
arr.push(result[key]);
}
arr.sort(function(a, b){
return a - b;
});
return arr;
}
/**
* Decode time range
* @param map The decode map
* @param timeStr The range string, like 2-5
*/
function decodeRangeTime(map, timeStr){
var times = timeStr.split('-');
times[0] = Number(times[0]);
times[1] = Number(times[1]);
if(times[0] > times[1]){
console.log("Error time range");
return null;
}
for(var i = times[0]; i <= times[1]; i++){
map[i] = i;
}
}
/**
* Compute the period timer
*/
function decodePeriodTime(map, timeStr, type){
var times = timeStr.split('/');
var min = Limit[type][0];
var max = Limit[type][1];
var remind = Number(times[0]);
var period = Number(times[1]);
if(period==0)
return;
for(var i = min; i <= max; i++){
if(i%period == remind)
map[i] = i;
}
}
/**
* Check if the numbers are valid
* @param nums The numbers array need to check
* @param min Minimus value
* @param max Maximam value
* @return If all the numbers are in the data range
*/
function checkNum(nums, min, max){
if(nums == null)
return false;
if(nums == -1)
return true;
for(var i = 0; i < nums.length; i++){
if(nums[i]<min || nums[i]>max)
return false;
}
return true;
}
/**
* Get the date limit of given month
* @param The given year
* @month The given month
* @return The date count of given month
*/
function getDomLimit(year, month){
var date = new Date(year, month+1, 0);
return date.getDate();
}
/**
* Create cronTrigger
* @param trigger The Cron Trigger string
* @return The Cron trigger
*/
function createTrigger(trigger, job){
return new CronTrigger(trigger, job);
}
module.exports.createTrigger = createTrigger;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* This is the class of the job used in schedule module
*/
var cronTrigger = require('./cronTrigger');
var simpleTrigger = require('./simpleTrigger');
var jobId = 1;
var SIMPLE_JOB = 1;
var CRON_JOB = 2;
var jobCount = 0;
var warnLimit = 500;
var logger = require('log4js').getLogger(__filename);
//For test
var lateCount = 0;
var Job = function(trigger, jobFunc, jobData){
this.data = (!!jobData)?jobData:null;
this.func = jobFunc;
if(typeof(trigger) == 'string'){
this.type = CRON_JOB;
this.trigger = cronTrigger.createTrigger(trigger, this);
}else if(typeof(trigger) == 'object'){
this.type = SIMPLE_JOB;
this.trigger = simpleTrigger.createTrigger(trigger, this);
}
this.id = jobId++;
this.runTime = 0;
};
var pro = Job.prototype;
/**
* Run the job code
*/
pro.run = function(){
try{
jobCount++;
this.runTime++;
var late = Date.now() - this.excuteTime();
if(late>warnLimit)
logger.warn('run Job count ' + jobCount + ' late :' + late + ' lateCount ' + (++lateCount));
this.func(this.data);
}catch(e){
logger.error("Job run error for exception ! " + e.stack);
}
};
/**
* Compute the next excution time
*/
pro.nextTime = function(){
return this.trigger.nextExcuteTime();
};
pro.excuteTime = function(){
return this.trigger.excuteTime();
};
/**
* The Interface to create Job
* @param trigger The trigger to use
* @param jobFunc The function the job to run
* @param jobDate The date the job use
* @return The new instance of the give job or null if fail
*/
function createJob(trigger, jobFunc, jobData){
return new Job(trigger, jobFunc, jobData);
}
module.exports.createJob = createJob;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* The PriorityQeueu class
*/
var PriorityQueue = function(comparator){
this.init(comparator);
}
var pro = PriorityQueue.prototype;
pro.init = function(comparator){
this._comparator = typeof(comparator)=='function'?comparator:this._defaultComparator;
this._queue = [];
this._tailPos = 0;
}
/**
* Return the size of the pirority queue
* @return PirorityQueue size
*/
pro.size = function(){
return this._tailPos;
};
/**
* Insert an element to the queue
* @param element The element to insert
*/
pro.offer = function(element){
var queue = this._queue;
var compare = this._comparator;
queue[this._tailPos++] = element;
var pos = this._tailPos-1;
while(pos > 0){
var parentPos = (pos%2==0)?(pos/2-1):(pos-1)/2;
if(compare(queue[parentPos], element)){
queue[pos] = queue[parentPos];
queue[parentPos] = element;
pos = parentPos;
}else{
break;
}
}
};
/**
* Get and remove the first element in the queue
* @return The first element
*/
pro.pop = function(){
var queue = this._queue;
var compare = this._comparator;
if(this._tailPos == 0)
return null;
var headNode = queue[0];
var tail = queue[this._tailPos - 1];
var pos = 0;
var left = pos*2 + 1;
var right = left + 1;
queue[pos] = tail;
this._tailPos--;
while(left < this._tailPos){
if(right<this._tailPos && compare(queue[left], queue[right]) && compare(queue[pos], queue[right])){
queue[pos] = queue[right];
queue[right] = tail;
pos = right;
}else if(compare(queue[pos],queue[left])){
queue[pos] = queue[left];
queue[left] = tail;
pos = left;
}else{
break;
}
left = pos*2 + 1;
right = left + 1;
}
return headNode;
};
/**
* Get but not remove the first element in the queue
* @return The first element
*/
pro.peek = function(){
if(this._tailPos == 0)
return null;
return this._queue[0];
}
pro._defaultComparator = function(a , b){
return a > b;
}
module.exports.createPriorityQueue = function(comparator){
return new PriorityQueue(comparator);
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* The main class and interface of the schedule module
*/
var PriorityQueue = require('./priorityQueue');
var Job = require('./job.js');
var timerCount = 0;
var logger = require('log4js').getLogger(__filename);
var map = {};
var queue = PriorityQueue.createPriorityQueue(comparator);
var jobId = 0;
var timer;
//The accuracy of the scheduler, it will affect the performance when the schedule tasks are
//crowded together
var accuracy = 10;
/**
* Schedule a new Job
*/
function scheduleJob(trigger, jobFunc, jobData){
var job = Job.createJob(trigger, jobFunc, jobData);
var excuteTime = job.excuteTime();
var id = job.id;
map[id] = job;
var element = {
id : id,
time : excuteTime
};
var curJob = queue.peek();
if(!curJob || excuteTime < curJob.time){
queue.offer(element);
setTimer(job);
return job.id;
}
queue.offer(element);
return job.id;
}
/**
* Cancel Job
*/
function cancelJob(id){
var curJob = queue.peek();
if(curJob && id === curJob.id){ // to avoid queue.peek() is null
queue.pop();
delete map[id];
clearTimeout(timer);
excuteJob();
}
delete map[id];
return true;
}
/**
* Clear last timeout and schedule the next job, it will automaticly run the job that
* need to run now
* @param job The job need to schedule
* @return void
*/
function setTimer(job){
clearTimeout(timer);
timer = setTimeout(excuteJob, job.excuteTime()-Date.now());
}
/**
* The function used to ran the schedule job, and setTimeout for next running job
*/
function excuteJob(){
var job = peekNextJob();
var nextJob;
while(!!job && (job.excuteTime()-Date.now())<accuracy){
job.run();
queue.pop();
var nextTime = job.nextTime();
if(nextTime === null){
delete map[job.id];
}else{
queue.offer({id:job.id, time: nextTime});
}
job = peekNextJob();
}
//If all the job have been canceled
if(!job)
return;
//Run next schedule
setTimer(job);
}
/**
* Return, but not remove the next valid job
* @return Next valid job
*/
function peekNextJob(){
if(queue.size() <= 0)
return null;
var job = null;
do{
job = map[queue.peek().id];
if(!job) queue.pop();
}while(!job && queue.size() > 0);
return (!!job)?job:null;
}
/**
* Return and remove the next valid job
* @return Next valid job
*/
function getNextJob(){
var job = null;
while(!job && queue.size() > 0){
var id = queue.pop().id;
job = map[id];
}
return (!!job)?job:null;
}
function comparator(e1, e2){
return e1.time > e2.time;
}
module.exports.scheduleJob = scheduleJob;
module.exports.cancelJob = cancelJob;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 1 1 1 1 1 1 1 | /** * This is the tirgger that use an object as trigger. */ var SKIP_OLD_JOB = false; /** * The constructor of simple trigger */ var SimpleTrigger = function(trigger, job){ this.nextTime = (!!trigger.start)?trigger.start:Date.now(); //The rec this.period = (!!trigger.period)?trigger.period:-1; //The running count of the job, -1 means no limit this.count = (!!trigger.count)?trigger.count:-1; this.job = job; }; var pro = SimpleTrigger.prototype; /** * Get the current excuteTime of rigger */ pro.excuteTime = function(){ return this.nextTime; }; /** * Get the next excuteTime of the trigger, and set the trigger's excuteTime * @return Next excute time */ pro.nextExcuteTime = function(){ var period = this.period; if((this.count > 0 && this.count <= this.job.runTime) || period <= 0) return null; this.nextTime += period; if(SKIP_OLD_JOB && this.nextTime < Date.now()){ this.nextTime += Math.floor((Date.now()-this.nextTime)/period) * period; } return this.nextTime; }; /** * Create Simple trigger */ function createTrigger(trigger, job){ return new SimpleTrigger(trigger, job); } module.exports.createTrigger = createTrigger; |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) |
| 1 2 | 1 | module.exports = require('./lib/loader');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| loader.js | 17.54% | (10 / 57) | 0% | (0 / 26) | 0% | (0 / 8) | 17.54% | (10 / 57) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 1 1 1 1 1 1 1 1 1 1 | /**
* Loader Module
*/
var fs = require('fs');
var path = require('path');
/**
* Load modules under the path.
* If the module is a function, loader would treat it as a factory function
* and invoke it with the context parameter to get a instance of the module.
* Else loader would just require the module.
* Module instance can specify a name property and it would use file name as
* the default name if there is no name property. All loaded modules under the
* path would be add to an empty root object with the name as the key.
*
* @param {String} mpath the path of modules. Load all the files under the
* path, but *not* recursively if the path contain
* any sub-directory.
* @param {Object} context the context parameter that would be pass to the
* module factory function.
* @return {Object} module that has loaded.
*/
module.exports.load = function(mpath, context) {
if(!mpath) {
throw new Error('opts or opts.path should not be empty.');
}
try {
mpath = fs.realpathSync(mpath);
} catch(err) {
throw err;
}
if(!isDir(mpath)) {
throw new Error('path should be directory.');
}
return loadPath(mpath, context);
};
var loadFile = function(fp, context) {
var m = requireUncached(fp);
if(!m) {
return;
}
if(typeof m === 'function') {
// if the module provides a factory function
// then invoke it to get a instance
m = m(context);
}
return m;
};
var loadPath = function(path, context) {
var files = fs.readdirSync(path);
if(files.length === 0) {
console.warn('path is empty, path:' + path);
return;
}
if(path.charAt(path.length - 1) !== '/') {
path += '/';
}
var fp, fn, m, res = {};
for(var i=0, l=files.length; i<l; i++) {
fn = files[i];
fp = path + fn;
if(!isFile(fp) || !checkFileType(fn, '.js')) {
// only load js file type
continue;
}
m = loadFile(fp, context);
if(!m) {
continue;
}
var name = m.name || getFileName(fn, '.js'.length);
res[name] = m;
}
return res;
};
/**
* Check file suffix
* @param fn {String} file name
* @param suffix {String} suffix string, such as .js, etc.
*/
var checkFileType = function(fn, suffix) {
if(suffix.charAt(0) !== '.') {
suffix = '.' + suffix;
}
if(fn.length <= suffix.length) {
return false;
}
var str = fn.substring(fn.length - suffix.length).toLowerCase();
suffix = suffix.toLowerCase();
return str === suffix;
};
var isFile = function(path) {
return fs.statSync(path).isFile();
};
var isDir = function(path) {
return fs.statSync(path).isDirectory();
};
var getFileName = function(fp, suffixLength) {
var fn = path.basename(fp);
if(fn.length > suffixLength) {
return fn.substring(0, fn.length - suffixLength);
}
return fn;
};
var requireUncached = function(module){
delete require.cache[require.resolve(module)]
return require(module)
}
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) |
| 1 2 | 1 | module.exports = require('./lib/logger');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| logger.js | 35.92% | (51 / 142) | 11.76% | (10 / 85) | 31.58% | (6 / 19) | 35.92% | (51 / 142) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | 1 1 1 1 1 60 60 60 60 60 60 60 60 60 60 2100 60 420 4 4 4 4 4 4 4 4 4 60 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 4 1 4 1 1 1 | var log4js = require('log4js');
var fs = require('fs');
var util = require('util');
var funcs = {
'env': doEnv,
'args': doArgs,
'opts': doOpts
};
function getLogger(categoryName) {
var args = arguments;
var prefix = "";
for (var i = 1; i < args.length; i++) {
Iif (i !== args.length - 1)
prefix = prefix + args[i] + "] [";
else
prefix = prefix + args[i];
}
Eif (typeof categoryName === 'string') {
// category name is __filename then cut the prefix path
categoryName = categoryName.replace(process.cwd(), '');
}
var logger = log4js.getLogger(categoryName);
var pLogger = {};
for (var key in logger) {
pLogger[key] = logger[key];
}
['log', 'debug', 'info', 'warn', 'error', 'trace', 'fatal'].forEach(function(item) {
pLogger[item] = function() {
var p = "";
Eif (!process.env.RAW_MESSAGE) {
Eif (args.length > 1) {
p = "[" + prefix + "] ";
}
Iif (args.length && process.env.LOGGER_LINE) {
p = getLine() + ": " + p;
}
p = colorize(p, colours[item]);
}
Eif (args.length) {
arguments[0] = p + arguments[0];
}
logger[item].apply(logger, arguments);
}
});
return pLogger;
};
var configState = {};
function initReloadConfiguration(filename, reloadSecs) {
if (configState.timerId) {
clearInterval(configState.timerId);
delete configState.timerId;
}
configState.filename = filename;
configState.lastMTime = getMTime(filename);
configState.timerId = setInterval(reloadConfiguration, reloadSecs * 1000);
};
function getMTime(filename) {
var mtime;
try {
mtime = fs.statSync(filename).mtime;
} catch (e) {
throw new Error("Cannot find file with given path: " + filename);
}
return mtime;
};
function loadConfigurationFile(filename) {
if (filename) {
return JSON.parse(fs.readFileSync(filename, "utf8"));
}
return undefined;
};
function reloadConfiguration() {
var mtime = getMTime(configState.filename);
if (!mtime) {
return;
}
if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) {
configureOnceOff(loadConfigurationFile(configState.filename));
}
configState.lastMTime = mtime;
};
function configureOnceOff(config) {
if (config) {
try {
configureLevels(config.levels);
if (config.replaceConsole) {
log4js.replaceConsole();
} else {
log4js.restoreConsole();
}
} catch (e) {
throw new Error(
"Problem reading log4js config " + util.inspect(config) +
". Error was \"" + e.message + "\" (" + e.stack + ")"
);
}
}
};
function configureLevels(levels) {
if (levels) {
for (var category in levels) {
if (levels.hasOwnProperty(category)) {
log4js.getLogger(category).setLevel(levels[category]);
}
}
}
};
/**
* Configure the logger.
* Configure file just like log4js.json. And support ${scope:arg-name} format property setting.
* It can replace the placeholder in runtime.
* scope can be:
* env: environment variables, such as: env:PATH
* args: command line arguments, such as: args:1
* opts: key/value from opts argument of configure function
*
* @param {String|Object} config configure file name or configure object
* @param {Object} opts options
* @return {Void}
*/
function configure(config, opts) {
var filename = config;
config = config || process.env.LOG4JS_CONFIG;
opts = opts || {};
if (typeof config === 'string') {
config = JSON.parse(fs.readFileSync(config, "utf8"));
}
if (config) {
config = replaceProperties(config, opts);
}
if (config && config.lineDebug) {
process.env.LOGGER_LINE = true;
}
if (config && config.rawMessage) {
process.env.RAW_MESSAGE = true;
}
if (filename && config && config.reloadSecs) {
initReloadConfiguration(filename, config.reloadSecs);
}
// config object could not turn on the auto reload configure file in log4js
log4js.configure(config, opts);
};
function replaceProperties(configObj, opts) {
if (configObj instanceof Array) {
for (var i = 0, l = configObj.length; i < l; i++) {
configObj[i] = replaceProperties(configObj[i], opts);
}
} else if (typeof configObj === 'object') {
var field;
for (var f in configObj) {
if (!configObj.hasOwnProperty(f)) {
continue;
}
field = configObj[f];
if (typeof field === 'string') {
configObj[f] = doReplace(field, opts);
} else if (typeof field === 'object') {
configObj[f] = replaceProperties(field, opts);
}
}
}
return configObj;
}
function doReplace(src, opts) {
if (!src) {
return src;
}
var ptn = /\$\{(.*?)\}/g;
var m, pro, ts, scope, name, defaultValue, func, res = '',
lastIndex = 0;
while ((m = ptn.exec(src))) {
pro = m[1];
ts = pro.split(':');
if (ts.length !== 2 && ts.length !== 3) {
res += pro;
continue;
}
scope = ts[0];
name = ts[1];
if (ts.length === 3) {
defaultValue = ts[2];
}
func = funcs[scope];
if (!func && typeof func !== 'function') {
res += pro;
continue;
}
res += src.substring(lastIndex, m.index);
lastIndex = ptn.lastIndex;
res += (func(name, opts) || defaultValue);
}
if (lastIndex < src.length) {
res += src.substring(lastIndex);
}
return res;
}
function doEnv(name) {
return process.env[name];
}
function doArgs(name) {
return process.argv[name];
}
function doOpts(name, opts) {
return opts ? opts[name] : undefined;
}
function getLine() {
var e = new Error();
// now magic will happen: get line number from callstack
var line = e.stack.split('\n')[3].split(':')[1];
return line;
}
function colorizeStart(style) {
return style ? '\x1B[' + styles[style][0] + 'm' : '';
}
function colorizeEnd(style) {
return style ? '\x1B[' + styles[style][1] + 'm' : '';
}
/**
* Taken from masylum's fork (https://github.com/masylum/log4js-node)
*/
function colorize(str, style) {
return colorizeStart(style) + str + colorizeEnd(style);
}
var styles = {
//styles
'bold': [1, 22],
'italic': [3, 23],
'underline': [4, 24],
'inverse': [7, 27],
//grayscale
'white': [37, 39],
'grey': [90, 39],
'black': [90, 39],
//colors
'blue': [34, 39],
'cyan': [36, 39],
'green': [32, 39],
'magenta': [35, 39],
'red': [31, 39],
'yellow': [33, 39]
};
var colours = {
'all': "grey",
'trace': "blue",
'debug': "cyan",
'info': "green",
'warn': "yellow",
'error': "red",
'fatal': "magenta",
'off': "grey"
};
module.exports = {
getLogger: getLogger,
getDefaultLogger: log4js.getDefaultLogger,
addAppender: log4js.addAppender,
loadAppender: log4js.loadAppender,
clearAppenders: log4js.clearAppenders,
configure: configure,
replaceConsole: log4js.replaceConsole,
restoreConsole: log4js.restoreConsole,
levels: log4js.levels,
setGlobalLogLevel: log4js.setGlobalLogLevel,
layouts: log4js.layouts,
appenders: log4js.appenders
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| connect-logger.js | 9.38% | (6 / 64) | 0% | (0 / 76) | 0% | (0 / 10) | 10.71% | (6 / 56) | |
| date_format.js | 94.59% | (35 / 37) | 50% | (5 / 10) | 100% | (4 / 4) | 94.59% | (35 / 37) | |
| layouts.js | 32.23% | (39 / 121) | 14.58% | (7 / 48) | 23.53% | (8 / 34) | 32.5% | (39 / 120) | |
| levels.js | 66.67% | (18 / 27) | 41.67% | (5 / 12) | 66.67% | (4 / 6) | 66.67% | (18 / 27) | |
| log4js.js | 53.89% | (90 / 167) | 40.85% | (29 / 71) | 46.15% | (18 / 39) | 56.25% | (90 / 160) | |
| logger.js | 86.05% | (37 / 43) | 50% | (6 / 12) | 54.55% | (6 / 11) | 86.05% | (37 / 43) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | 1 1 1 1 1 1 | "use strict";
var levels = require("./levels");
var DEFAULT_FORMAT = ':remote-addr - -' +
' ":method :url HTTP/:http-version"' +
' :status :content-length ":referrer"' +
' ":user-agent"';
/**
* Log requests with the given `options` or a `format` string.
*
* Options:
*
* - `format` Format string, see below for tokens
* - `level` A log4js levels instance. Supports also 'auto'
*
* Tokens:
*
* - `:req[header]` ex: `:req[Accept]`
* - `:res[header]` ex: `:res[Content-Length]`
* - `:http-version`
* - `:response-time`
* - `:remote-addr`
* - `:date`
* - `:method`
* - `:url`
* - `:referrer`
* - `:user-agent`
* - `:status`
*
* @param {String|Function|Object} format or options
* @return {Function}
* @api public
*/
function getLogger(logger4js, options) {
if ('object' == typeof options) {
options = options || {};
} else if (options) {
options = { format: options };
} else {
options = {};
}
var thislogger = logger4js
, level = levels.toLevel(options.level, levels.INFO)
, fmt = options.format || DEFAULT_FORMAT
, nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
return function (req, res, next) {
// mount safety
if (req._logging) return next();
// nologs
if (nolog && nolog.test(req.originalUrl)) return next();
if (thislogger.isLevelEnabled(level) || options.level === 'auto') {
var start = new Date()
, statusCode
, writeHead = res.writeHead
, end = res.end
, url = req.originalUrl;
// flag as logging
req._logging = true;
// proxy for statusCode.
res.writeHead = function(code, headers){
res.writeHead = writeHead;
res.writeHead(code, headers);
res.__statusCode = statusCode = code;
res.__headers = headers || {};
//status code response level handling
if(options.level === 'auto'){
level = levels.INFO;
if(code >= 300) level = levels.WARN;
if(code >= 400) level = levels.ERROR;
} else {
level = levels.toLevel(options.level, levels.INFO);
}
};
// proxy end to output a line to the provided logger.
res.end = function(chunk, encoding) {
res.end = end;
res.end(chunk, encoding);
res.responseTime = new Date() - start;
//status code response level handling
if(res.statusCode && options.level === 'auto'){
level = levels.INFO;
if(res.statusCode >= 300) level = levels.WARN;
if(res.statusCode >= 400) level = levels.ERROR;
}
if (thislogger.isLevelEnabled(level)) {
if (typeof fmt === 'function') {
var line = fmt(req, res, function(str){ return format(str, req, res); });
if (line) thislogger.log(level, line);
} else {
thislogger.log(level, format(fmt, req, res));
}
}
};
}
//ensure next gets always called
next();
};
}
/**
* Return formatted log line.
*
* @param {String} str
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @return {String}
* @api private
*/
function format(str, req, res) {
return str
.replace(':url', req.originalUrl)
.replace(':method', req.method)
.replace(':status', res.__statusCode || res.statusCode)
.replace(':response-time', res.responseTime)
.replace(':date', new Date().toUTCString())
.replace(':referrer', req.headers.referer || req.headers.referrer || '')
.replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor)
.replace(
':remote-addr', req.ip || req._remoteAddress || (
req.socket &&
(req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress))
))
.replace(':user-agent', req.headers['user-agent'] || '')
.replace(
':content-length',
(res._headers && res._headers['content-length']) ||
(res.__headers && res.__headers['Content-Length']) ||
'-'
)
.replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; })
.replace(/:res\[([^\]]+)\]/g, function(_, field){
return res._headers ?
(res._headers[field.toLowerCase()] || res.__headers[field])
: (res.__headers && res.__headers[field]);
});
}
/**
* Return RegExp Object about nolog
*
* @param {String} nolog
* @return {RegExp}
* @api private
*
* syntax
* 1. String
* 1.1 "\\.gif"
* NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga
* LOGGING http://example.com/hoge.agif
* 1.2 in "\\.gif|\\.jpg$"
* NOT LOGGING http://example.com/hoge.gif and
* http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga
* LOGGING http://example.com/hoge.agif,
* http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge
* 1.3 in "\\.(gif|jpe?g|png)$"
* NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg
* LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3
* 2. RegExp
* 2.1 in /\.(gif|jpe?g|png)$/
* SAME AS 1.3
* 3. Array
* 3.1 ["\\.jpg$", "\\.png", "\\.gif"]
* SAME AS "\\.jpg|\\.png|\\.gif"
*/
function createNoLogCondition(nolog) {
var regexp = null;
if (nolog) {
if (nolog instanceof RegExp) {
regexp = nolog;
}
if (typeof nolog === 'string') {
regexp = new RegExp(nolog);
}
if (Array.isArray(nolog)) {
var regexpsAsStrings = nolog.map(
function convertToStrings(o) {
return o.source ? o.source : o;
}
);
regexp = new RegExp(regexpsAsStrings.join('|'));
}
}
return regexp;
}
exports.connectLogger = getLogger;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 1 1 1 1 1 32 32 8 32 1 28 1 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 | "use strict";
exports.ISO8601_FORMAT = "yyyy-MM-dd hh:mm:ss.SSS";
exports.ISO8601_WITH_TZ_OFFSET_FORMAT = "yyyy-MM-ddThh:mm:ssO";
exports.DATETIME_FORMAT = "dd MM yyyy hh:mm:ss.SSS";
exports.ABSOLUTETIME_FORMAT = "hh:mm:ss.SSS";
function padWithZeros(vNumber, width) {
var numAsString = vNumber + "";
while (numAsString.length < width) {
numAsString = "0" + numAsString;
}
return numAsString;
}
function addZero(vNumber) {
return padWithZeros(vNumber, 2);
}
/**
* Formats the TimeOffest
* Thanks to http://www.svendtofte.com/code/date_format/
* @private
*/
function offset(date) {
// Difference to Greenwich time (GMT) in hours
var os = Math.abs(date.getTimezoneOffset());
var h = String(Math.floor(os/60));
var m = String(os%60);
Eif (h.length == 1) {
h = "0" + h;
}
Eif (m.length == 1) {
m = "0" + m;
}
return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;
}
exports.asString = function(/*format,*/ date) {
var format = exports.ISO8601_FORMAT;
Iif (typeof(date) === "string") {
format = arguments[0];
date = arguments[1];
}
var vDay = addZero(date.getDate());
var vMonth = addZero(date.getMonth()+1);
var vYearLong = addZero(date.getFullYear());
var vYearShort = addZero(date.getFullYear().toString().substring(2,4));
var vYear = (format.indexOf("yyyy") > -1 ? vYearLong : vYearShort);
var vHour = addZero(date.getHours());
var vMinute = addZero(date.getMinutes());
var vSecond = addZero(date.getSeconds());
var vMillisecond = padWithZeros(date.getMilliseconds(), 3);
var vTimeZone = offset(date);
var formatted = format
.replace(/dd/g, vDay)
.replace(/MM/g, vMonth)
.replace(/y{1,4}/g, vYear)
.replace(/hh/g, vHour)
.replace(/mm/g, vMinute)
.replace(/ss/g, vSecond)
.replace(/SSS/g, vMillisecond)
.replace(/O/g, vTimeZone);
return formatted;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | 1 1 8 24 24 1 8 8 1 1 4 1 4 1 4 1 4 4 1 1 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | "use strict";
var dateFormat = require('./date_format')
, os = require('os')
, eol = os.EOL || '\n'
, util = require('util')
, replacementRegExp = /%[sdj]/g
, layoutMakers = {
"messagePassThrough": function() { return messagePassThroughLayout; },
"basic": function() { return basicLayout; },
"colored": function() { return colouredLayout; },
"coloured": function() { return colouredLayout; },
"pattern": function (config) {
return patternLayout(config && config.pattern, config && config.tokens);
}
}
, colours = {
ALL: "grey",
TRACE: "blue",
DEBUG: "cyan",
INFO: "green",
WARN: "yellow",
ERROR: "red",
FATAL: "magenta",
OFF: "grey"
};
function wrapErrorsWithInspect(items) {
return items.map(function(item) {
Iif ((item instanceof Error) && item.stack) {
return { inspect: function() { return util.format(item) + '\n' + item.stack; } };
} else {
return item;
}
});
}
function formatLogData(logData) {
var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments);
return util.format.apply(util, wrapErrorsWithInspect(data));
}
var styles = {
//styles
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
//grayscale
'white' : [37, 39],
'grey' : [90, 39],
'black' : [90, 39],
//colors
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
function colorizeStart(style) {
return style ? '\x1B[' + styles[style][0] + 'm' : '';
}
function colorizeEnd(style) {
return style ? '\x1B[' + styles[style][1] + 'm' : '';
}
/**
* Taken from masylum's fork (https://github.com/masylum/log4js-node)
*/
function colorize (str, style) {
return colorizeStart(style) + str + colorizeEnd(style);
}
function timestampLevelAndCategory(loggingEvent, colour) {
var output = colorize(
formatLogData(
'[%s] [%s] %s - '
, dateFormat.asString(loggingEvent.startTime)
, loggingEvent.level
, loggingEvent.categoryName
)
, colour
);
return output;
}
/**
* BasicLayout is a simple layout for storing the logs. The logs are stored
* in following format:
* <pre>
* [startTime] [logLevel] categoryName - message\n
* </pre>
*
* @author Stephan Strittmatter
*/
function basicLayout (loggingEvent) {
return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data);
}
/**
* colouredLayout - taken from masylum's fork.
* same as basicLayout, but with colours.
*/
function colouredLayout (loggingEvent) {
return timestampLevelAndCategory(
loggingEvent,
colours[loggingEvent.level.toString()]
) + formatLogData(loggingEvent.data);
}
function messagePassThroughLayout (loggingEvent) {
return formatLogData(loggingEvent.data);
}
/**
* PatternLayout
* Format for specifiers is %[padding].[truncation][field]{[format]}
* e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
* Fields can be any of:
* - %r time in toLocaleTimeString format
* - %p log level
* - %c log category
* - %h hostname
* - %m log data
* - %d date in various formats
* - %% %
* - %n newline
* - %z pid
* - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
* You can use %[ and %] to define a colored block.
*
* Tokens are specified as simple key:value objects.
* The key represents the token name whereas the value can be a string or function
* which is called to extract the value to put in the log message. If token is not
* found, it doesn't replace the field.
*
* A sample token would be: { "pid" : function() { return process.pid; } }
*
* Takes a pattern string, array of tokens and returns a layout function.
* @param {String} Log format pattern String
* @param {object} map object of different tokens
* @return {Function}
* @author Stephan Strittmatter
* @author Jan Schmidle
*/
function patternLayout (pattern, tokens) {
var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzx%])(\{([^\}]+)\})?|([^%]+)/;
pattern = pattern || TTCC_CONVERSION_PATTERN;
function categoryName(loggingEvent, specifier) {
var loggerName = loggingEvent.categoryName;
if (specifier) {
var precision = parseInt(specifier, 10);
var loggerNameBits = loggerName.split(".");
if (precision < loggerNameBits.length) {
loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
}
}
return loggerName;
}
function formatAsDate(loggingEvent, specifier) {
var format = dateFormat.ISO8601_FORMAT;
if (specifier) {
format = specifier;
// Pick up special cases
if (format == "ISO8601") {
format = dateFormat.ISO8601_FORMAT;
} else if (format == "ISO8601_WITH_TZ_OFFSET") {
format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
} else if (format == "ABSOLUTE") {
format = dateFormat.ABSOLUTETIME_FORMAT;
} else if (format == "DATE") {
format = dateFormat.DATETIME_FORMAT;
}
}
// Format the date
return dateFormat.asString(format, loggingEvent.startTime);
}
function hostname() {
return os.hostname().toString();
}
function formatMessage(loggingEvent) {
return formatLogData(loggingEvent.data);
}
function endOfLine() {
return eol;
}
function logLevel(loggingEvent) {
return loggingEvent.level.toString();
}
function startTime(loggingEvent) {
return "" + loggingEvent.startTime.toLocaleTimeString();
}
function startColour(loggingEvent) {
return colorizeStart(colours[loggingEvent.level.toString()]);
}
function endColour(loggingEvent) {
return colorizeEnd(colours[loggingEvent.level.toString()]);
}
function percent() {
return '%';
}
function pid() {
return process.pid;
}
function userDefined(loggingEvent, specifier) {
if (typeof(tokens[specifier]) !== 'undefined') {
if (typeof(tokens[specifier]) === 'function') {
return tokens[specifier](loggingEvent);
} else {
return tokens[specifier];
}
}
return null;
}
var replacers = {
'c': categoryName,
'd': formatAsDate,
'h': hostname,
'm': formatMessage,
'n': endOfLine,
'p': logLevel,
'r': startTime,
'[': startColour,
']': endColour,
'z': pid,
'%': percent,
'x': userDefined
};
function replaceToken(conversionCharacter, loggingEvent, specifier) {
return replacers[conversionCharacter](loggingEvent, specifier);
}
function truncate(truncation, toTruncate) {
var len;
if (truncation) {
len = parseInt(truncation.substr(1), 10);
return toTruncate.substring(0, len);
}
return toTruncate;
}
function pad(padding, toPad) {
var len;
if (padding) {
if (padding.charAt(0) == "-") {
len = parseInt(padding.substr(1), 10);
// Right pad with spaces
while (toPad.length < len) {
toPad += " ";
}
} else {
len = parseInt(padding, 10);
// Left pad with spaces
while (toPad.length < len) {
toPad = " " + toPad;
}
}
}
return toPad;
}
return function(loggingEvent) {
var formattedString = "";
var result;
var searchString = pattern;
while ((result = regex.exec(searchString))) {
var matchedString = result[0];
var padding = result[1];
var truncation = result[2];
var conversionCharacter = result[3];
var specifier = result[5];
var text = result[6];
// Check if the pattern matched was just normal text
if (text) {
formattedString += "" + text;
} else {
// Create a raw replacement string based on the conversion
// character and specifier
var replacement =
replaceToken(conversionCharacter, loggingEvent, specifier) ||
matchedString;
// Format the replacement according to any padding or
// truncation specified
replacement = truncate(truncation, replacement);
replacement = pad(padding, replacement);
formattedString += replacement;
}
searchString = searchString.substr(result.index + result[0].length);
}
return formattedString;
};
}
module.exports = {
basicLayout: basicLayout,
messagePassThroughLayout: messagePassThroughLayout,
patternLayout: patternLayout,
colouredLayout: colouredLayout,
coloredLayout: colouredLayout,
layout: function(name, config) {
return layoutMakers[name] && layoutMakers[name](config);
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 1 8 8 1 14 14 10 10 10 4 1 12 1 8 8 1 1 1 | "use strict";
function Level(level, levelStr) {
this.level = level;
this.levelStr = levelStr;
}
/**
* converts given String to corresponding Level
* @param {String} sArg String value of Level OR Log4js.Level
* @param {Log4js.Level} defaultLevel default Level, if no String representation
* @return Level object
* @type Log4js.Level
*/
function toLevel(sArg, defaultLevel) {
Iif (!sArg) {
return defaultLevel;
}
if (typeof sArg == "string") {
var s = sArg.toUpperCase();
Eif (module.exports[s]) {
return module.exports[s];
} else {
return defaultLevel;
}
}
return toLevel(sArg.toString());
}
Level.prototype.toString = function() {
return this.levelStr;
};
Level.prototype.isLessThanOrEqualTo = function(otherLevel) {
Iif (typeof otherLevel === "string") {
otherLevel = toLevel(otherLevel);
}
return this.level <= otherLevel.level;
};
Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) {
if (typeof otherLevel === "string") {
otherLevel = toLevel(otherLevel);
}
return this.level >= otherLevel.level;
};
Level.prototype.isEqualTo = function(otherLevel) {
if (typeof otherLevel == "string") {
otherLevel = toLevel(otherLevel);
}
return this.level === otherLevel.level;
};
module.exports = {
ALL: new Level(Number.MIN_VALUE, "ALL"),
TRACE: new Level(5000, "TRACE"),
DEBUG: new Level(10000, "DEBUG"),
INFO: new Level(20000, "INFO"),
WARN: new Level(30000, "WARN"),
ERROR: new Level(40000, "ERROR"),
FATAL: new Level(50000, "FATAL"),
OFF: new Level(Number.MAX_VALUE, "OFF"),
toLevel: toLevel
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | 1 1 60 1 1 60 60 60 10 10 10 10 10 10 60 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | "use strict";
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview log4js is a library to log in JavaScript in similar manner
* than in log4j for Java. The API should be nearly the same.
*
* <h3>Example:</h3>
* <pre>
* var logging = require('log4js');
* //add an appender that logs all messages to stdout.
* logging.addAppender(logging.consoleAppender());
* //add an appender that logs "some-category" to a file
* logging.addAppender(logging.fileAppender("file.log"), "some-category");
* //get a logger
* var log = logging.getLogger("some-category");
* log.setLevel(logging.levels.TRACE); //set the Level
*
* ...
*
* //call the log
* log.trace("trace me" );
* </pre>
*
* NOTE: the authors below are the original browser-based log4js authors
* don't try to contact them about bugs in this version :)
* @version 1.0
* @author Stephan Strittmatter - http://jroller.com/page/stritti
* @author Seth Chisamore - http://www.chisamore.com
* @since 2005-05-20
* @static
* Website: http://log4js.berlios.de
*/
var events = require('events')
, async = require('async')
, fs = require('fs')
, path = require('path')
, util = require('util')
, layouts = require('./layouts')
, levels = require('./levels')
, loggerModule = require('./logger')
, LoggingEvent = loggerModule.LoggingEvent
, Logger = loggerModule.Logger
, ALL_CATEGORIES = '[all]'
, appenders = {}
, loggers = {}
, appenderMakers = {}
, appenderShutdowns = {}
, defaultConfig = {
appenders: [
{ type: "console" }
],
replaceConsole: false
};
function hasLogger(logger) {
return loggers.hasOwnProperty(logger);
}
function getBufferedLogger(categoryName) {
var base_logger = getLogger(categoryName);
var logger = {};
logger.temp = [];
logger.target = base_logger;
logger.flush = function () {
for (var i = 0; i < logger.temp.length; i++) {
var log = logger.temp[i];
logger.target[log.level](log.message);
delete logger.temp[i];
}
};
logger.trace = function (message) { logger.temp.push({level: 'trace', message: message}); };
logger.debug = function (message) { logger.temp.push({level: 'debug', message: message}); };
logger.info = function (message) { logger.temp.push({level: 'info', message: message}); };
logger.warn = function (message) { logger.temp.push({level: 'warn', message: message}); };
logger.error = function (message) { logger.temp.push({level: 'error', message: message}); };
logger.fatal = function (message) { logger.temp.push({level: 'fatal', message: message}); };
return logger;
}
/**
* Get a logger instance. Instance is cached on categoryName level.
* @param {String} categoryName name of category to log to.
* @return {Logger} instance of logger for the category
* @static
*/
function getLogger (categoryName) {
// Use default logger if categoryName is not specified or invalid
Iif (typeof categoryName !== "string") {
categoryName = Logger.DEFAULT_CATEGORY;
}
var appenderList;
if (!hasLogger(categoryName)) {
// Create the logger for this name if it doesn't already exist
loggers[categoryName] = new Logger(categoryName);
Iif (appenders[categoryName]) {
appenderList = appenders[categoryName];
appenderList.forEach(function(appender) {
loggers[categoryName].addListener("log", appender);
});
}
Eif (appenders[ALL_CATEGORIES]) {
appenderList = appenders[ALL_CATEGORIES];
appenderList.forEach(function(appender) {
loggers[categoryName].addListener("log", appender);
});
}
}
return loggers[categoryName];
}
/**
* args are appender, then zero or more categories
*/
function addAppender () {
var args = Array.prototype.slice.call(arguments);
var appender = args.shift();
Eif (args.length === 0 || args[0] === undefined) {
args = [ ALL_CATEGORIES ];
}
//argument may already be an array
Iif (Array.isArray(args[0])) {
args = args[0];
}
args.forEach(function(category) {
addAppenderToCategory(appender, category);
Eif (category === ALL_CATEGORIES) {
addAppenderToAllLoggers(appender);
} else if (hasLogger(category)) {
loggers[category].addListener("log", appender);
}
});
}
function addAppenderToAllLoggers(appender) {
for (var logger in loggers) {
if (hasLogger(logger)) {
loggers[logger].addListener("log", appender);
}
}
}
function addAppenderToCategory(appender, category) {
Eif (!appenders[category]) {
appenders[category] = [];
}
appenders[category].push(appender);
}
function clearAppenders () {
appenders = {};
for (var logger in loggers) {
if (hasLogger(logger)) {
loggers[logger].removeAllListeners("log");
}
}
}
function configureAppenders(appenderList, options) {
clearAppenders();
Eif (appenderList) {
appenderList.forEach(function(appenderConfig) {
loadAppender(appenderConfig.type);
var appender;
appenderConfig.makers = appenderMakers;
try {
appender = appenderMakers[appenderConfig.type](appenderConfig, options);
addAppender(appender, appenderConfig.category);
} catch(e) {
throw new Error("log4js configuration problem for " + util.inspect(appenderConfig), e);
}
});
}
}
function configureLevels(levels) {
Iif (levels) {
for (var category in levels) {
if (levels.hasOwnProperty(category)) {
if(category === ALL_CATEGORIES) {
setGlobalLogLevel(levels[category]);
}
getLogger(category).setLevel(levels[category]);
}
}
}
}
function setGlobalLogLevel(level) {
Logger.prototype.level = levels.toLevel(level, levels.TRACE);
}
/**
* Get the default logger instance.
* @return {Logger} instance of default logger
* @static
*/
function getDefaultLogger () {
return getLogger(Logger.DEFAULT_CATEGORY);
}
var configState = {};
function loadConfigurationFile(filename) {
Iif (filename) {
return JSON.parse(fs.readFileSync(filename, "utf8"));
}
return undefined;
}
function configureOnceOff(config, options) {
Eif (config) {
try {
configureAppenders(config.appenders, options);
configureLevels(config.levels);
Iif (config.replaceConsole) {
replaceConsole();
} else {
restoreConsole();
}
} catch (e) {
throw new Error(
"Problem reading log4js config " + util.inspect(config) +
". Error was \"" + e.message + "\" (" + e.stack + ")"
);
}
}
}
function reloadConfiguration() {
var mtime = getMTime(configState.filename);
if (!mtime) return;
if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) {
configureOnceOff(loadConfigurationFile(configState.filename));
}
configState.lastMTime = mtime;
}
function getMTime(filename) {
var mtime;
try {
mtime = fs.statSync(configState.filename).mtime;
} catch (e) {
getLogger('log4js').warn('Failed to load configuration file ' + filename);
}
return mtime;
}
function initReloadConfiguration(filename, options) {
if (configState.timerId) {
clearInterval(configState.timerId);
delete configState.timerId;
}
configState.filename = filename;
configState.lastMTime = getMTime(filename);
configState.timerId = setInterval(reloadConfiguration, options.reloadSecs*1000);
}
function configure(configurationFileOrObject, options) {
var config = configurationFileOrObject;
config = config || process.env.LOG4JS_CONFIG;
options = options || {};
Eif (config === undefined || config === null || typeof(config) === 'string') {
Iif (options.reloadSecs) {
initReloadConfiguration(config, options);
}
config = loadConfigurationFile(config) || defaultConfig;
} else {
if (options.reloadSecs) {
getLogger('log4js').warn(
'Ignoring configuration reload parameter for "object" configuration.'
);
}
}
configureOnceOff(config, options);
}
var originalConsoleFunctions = {
log: console.log,
debug: console.debug,
info: console.info,
warn: console.warn,
error: console.error
};
function replaceConsole(logger) {
function replaceWith(fn) {
return function() {
fn.apply(logger, arguments);
};
}
logger = logger || getLogger("console");
['log','debug','info','warn','error'].forEach(function (item) {
console[item] = replaceWith(item === 'log' ? logger.info : logger[item]);
});
}
function restoreConsole() {
['log', 'debug', 'info', 'warn', 'error'].forEach(function (item) {
console[item] = originalConsoleFunctions[item];
});
}
/**
* Load an appenderModule based on the provided appender filepath. Will first
* check if the appender path is a subpath of the log4js "lib/appenders" directory.
* If not, it will attempt to load the the appender as complete path.
*
* @param {string} appender The filepath for the appender.
* @returns {Object|null} The required appender or null if appender could not be loaded.
* @private
*/
function requireAppender(appender) {
var appenderModule;
try {
appenderModule = require('./appenders/' + appender);
} catch (e) {
appenderModule = require(appender);
}
return appenderModule;
}
/**
* Load an appender. Provided the appender path to be loaded. If appenderModule is defined,
* it will be used in place of requiring the appender module.
*
* @param {string} appender The path to the appender module.
* @param {Object|void} [appenderModule] The pre-required appender module. When provided,
* instead of requiring the appender by its path, this object will be used.
* @returns {void}
* @private
*/
function loadAppender(appender, appenderModule) {
appenderModule = appenderModule || requireAppender(appender);
Iif (!appenderModule) {
throw new Error("Invalid log4js appender: " + util.inspect(appender));
}
module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule);
Iif (appenderModule.shutdown) {
appenderShutdowns[appender] = appenderModule.shutdown.bind(appenderModule);
}
appenderMakers[appender] = appenderModule.configure.bind(appenderModule);
}
/**
* Shutdown all log appenders. This will first disable all writing to appenders
* and then call the shutdown function each appender.
*
* @params {Function} cb - The callback to be invoked once all appenders have
* shutdown. If an error occurs, the callback will be given the error object
* as the first argument.
* @returns {void}
*/
function shutdown(cb) {
// First, disable all writing to appenders. This prevents appenders from
// not being able to be drained because of run-away log writes.
loggerModule.disableAllLogWrites();
// Next, get all the shutdown functions for appenders as an array.
var shutdownFunctions = Object.keys(appenderShutdowns).reduce(
function(accum, category) {
return accum.concat(appenderShutdowns[category]);
}, []);
// Call each of the shutdown functions.
async.each(
shutdownFunctions,
function(shutdownFn, done) {
shutdownFn(done);
},
cb
);
}
module.exports = {
getBufferedLogger: getBufferedLogger,
getLogger: getLogger,
getDefaultLogger: getDefaultLogger,
hasLogger: hasLogger,
addAppender: addAppender,
loadAppender: loadAppender,
clearAppenders: clearAppenders,
configure: configure,
shutdown: shutdown,
replaceConsole: replaceConsole,
restoreConsole: restoreConsole,
levels: levels,
setGlobalLogLevel: setGlobalLogLevel,
layouts: layouts,
appenders: {},
appenderMakers: appenderMakers,
connectLogger: require('./connect-logger').connectLogger
};
//set ourselves up
configure();
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 1 1 1 4 4 4 4 4 1 10 10 1 1 1 1 1 1 4 4 4 4 1 8 1 6 6 6 4 4 4 4 1 1 1 1 1 1 | "use strict";
var levels = require('./levels')
, util = require('util')
, events = require('events')
, DEFAULT_CATEGORY = '[default]';
var logWritesEnabled = true;
/**
* Models a logging event.
* @constructor
* @param {String} categoryName name of category
* @param {Log4js.Level} level level of message
* @param {Array} data objects to log
* @param {Log4js.Logger} logger the associated logger
* @author Seth Chisamore
*/
function LoggingEvent (categoryName, level, data, logger) {
this.startTime = new Date();
this.categoryName = categoryName;
this.data = data;
this.level = level;
this.logger = logger;
}
/**
* Logger to log messages.
* use {@see Log4js#getLogger(String)} to get an instance.
* @constructor
* @param name name of category to log to
* @author Stephan Strittmatter
*/
function Logger (name, level) {
this.category = name || DEFAULT_CATEGORY;
Iif (level) {
this.setLevel(level);
}
}
util.inherits(Logger, events.EventEmitter);
Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY;
Logger.prototype.level = levels.TRACE;
Logger.prototype.setLevel = function(level) {
this.level = levels.toLevel(level, this.level || levels.TRACE);
};
Logger.prototype.removeLevel = function() {
delete this.level;
};
Logger.prototype.log = function() {
var args = Array.prototype.slice.call(arguments)
, logLevel = levels.toLevel(args.shift())
, loggingEvent;
Eif (this.isLevelEnabled(logLevel)) {
loggingEvent = new LoggingEvent(this.category, logLevel, args, this);
this.emit("log", loggingEvent);
}
};
Logger.prototype.isLevelEnabled = function(otherLevel) {
return this.level.isLessThanOrEqualTo(otherLevel);
};
['Trace','Debug','Info','Warn','Error','Fatal'].forEach(
function(levelString) {
var level = levels.toLevel(levelString);
Logger.prototype['is'+levelString+'Enabled'] = function() {
return this.isLevelEnabled(level);
};
Logger.prototype[levelString.toLowerCase()] = function () {
Eif (logWritesEnabled && this.isLevelEnabled(level)) {
var args = Array.prototype.slice.call(arguments);
args.unshift(level);
Logger.prototype.log.apply(this, args);
}
};
}
);
/**
* Disable all log writes.
* @returns {void}
*/
function disableAllLogWrites() {
logWritesEnabled = false;
}
/**
* Enable log writes.
* @returns {void}
*/
function enableAllLogWrites() {
logWritesEnabled = true;
}
exports.LoggingEvent = LoggingEvent;
exports.Logger = Logger;
exports.disableAllLogWrites = disableAllLogWrites;
exports.enableAllLogWrites = enableAllLogWrites;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| console.js | 91.67% | (11 / 12) | 75% | (3 / 4) | 100% | (3 / 3) | 91.67% | (11 / 12) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 1 1 1 1 4 1 1 1 1 1 1 | "use strict";
var layouts = require('../layouts')
, consoleLog = console.log.bind(console);
function consoleAppender (layout) {
layout = layout || layouts.colouredLayout;
return function(loggingEvent) {
consoleLog(layout(loggingEvent));
};
}
function configure(config) {
var layout;
Iif (config.layout) {
layout = layouts.layout(config.layout.type, config.layout);
}
return consoleAppender(layout);
}
exports.appender = consoleAppender;
exports.configure = configure;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 100% | (2 / 2) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (2 / 2) |
| 1 2 3 4 | 1 1 | module.exports.psmonitor=require('./lib/processMonitor');
module.exports.sysmonitor=require('./lib/systemMonitor');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| processMonitor.js | 8% | (4 / 50) | 0% | (0 / 14) | 0% | (0 / 4) | 8.16% | (4 / 49) | |
| systemMonitor.js | 45.83% | (22 / 48) | 0% | (0 / 8) | 0% | (0 / 7) | 50% | (22 / 44) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 1 1 1 1 | /**
*Module dependencies
*/
var exec = require('child_process').exec
, spawn = require('child_process').spawn
, util = require('../utils/util');
/**
* Expose 'getPsInfo' constructor
*/
module.exports.getPsInfo = getPsInfo;
/**
* get the process information by command 'ps auxw | grep serverId | grep pid'
*
* @param {Object} param
* @param {Function} callback
* @api public
*/
function getPsInfo(param, callback) {
if (process.platform === 'windows') return;
var pid = param.pid;
var cmd = "ps auxw | grep " + pid + " | grep -v 'grep'";
//var cmd = "ps auxw | grep -E '.+?\\s+" + pid + "\\s+'" ;
exec(cmd, function(err, output) {
if (!!err) {
if (err.code === 1) {
console.log('the content is null!');
} else {
console.error('getPsInfo failed! ' + err.stack);
}
callback(err, null);
return;
}
format(param, output, callback);
});
};
/**
* convert serverInfo to required format, and the callback will handle the serverInfo
*
* @param {Object} param, contains serverId etc
* @param {String} data, the output if the command 'ps'
* @param {Function} cb
* @api private
*/
function format(param, data, cb) {
var time = util.formatTime(new Date());
var outArray = data.toString().replace(/^\s+|\s+$/g,"").split(/\s+/);
var outValueArray = [];
for (var i = 0; i < outArray.length; i++) {
if ((!isNaN(outArray[i]))) {
outValueArray.push(outArray[i]);
}
}
var ps = {};
ps.time = time;
ps.serverId = param.serverId;
ps.serverType = ps.serverId.split('-')[0];
var pid = ps.pid = param.pid;
ps.cpuAvg = outValueArray[1];
ps.memAvg = outValueArray[2];
ps.vsz = outValueArray[3];
ps.rss = outValueArray[4];
outValueArray = [];
if (process.platform === 'darwin') {
ps.usr = 0;
ps.sys = 0;
ps.gue = 0;
cb(null, ps);
return;
}
exec('pidstat -p ' + pid, function(err, output) {
if (!!err) {
console.error('the command pidstat failed! ', err.stack);
return;
}
var outArray = output.toString().replace(/^\s+|\s+$/g,"").split(/\s+/);
for (var i = 0; i < outArray.length; i++) {
if ((!isNaN(outArray[i]))) {
outValueArray.push(outArray[i]);
}
}
ps.usr = outValueArray[1];
ps.sys = outValueArray[2];
ps.gue = outValueArray[3];
cb(null, ps);
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* Module dependencies
*/
var os = require('os')
, util = require('../utils/util')
, exec = require('child_process').exec;
var info = {};
/*
* Expose 'getSysInfo' constructor
*/
module.exports.getSysInfo = getSysInfo;
/**
* get information of operating-system
*
* @param {Function} callback
* @api public
*/
function getSysInfo(callback) {
if (process.platform === 'windows') return;
var reData = getBasicInfo();
exec('iostat ', function(err, output) {
if (!!err) {
console.error('getSysInfo failed! ' + err.stack);
callback(err, reData);
} else {
reData.iostat = format(output);
callback(null, reData);
}
});
};
/**
* analysis the disk i/o data,return a map contains kb_read,kb_wrtn ect.
*
* @param {String} data, the output of the command 'iostat'
* @api private
*/
function format(data) {
var time = util.formatTime(new Date());
var output_array = data.toString().replace(/^\s+|\s+$/g,"").split(/\s+/);
var output_values = [];
for (var i = 0, counter = 0; i < output_array.length; i++) {
if(!isNaN(output_array[i])) {
output_values[counter] = parseFloat(output_array[i]);
counter++;
}
}
if (output_values.length > 0) {
output_hash = {
date: time,
disk: {
kb_read: output_values[9],
kb_wrtn: output_values[10],
kb_read_per: output_values[7],
kb_wrtn_per: output_values[8],
tps: output_values[6]
},
cpu: {
cpu_user: output_values[0],
cpu_nice: output_values[1],
cpu_system: output_values[2],
cpu_iowait: output_values[3],
cpu_steal: output_values[4],
cpu_idle: output_values[5]
}
}
return output_hash;
}
};
/**
* get basic information of operating-system
*
* @return {Object} result
* @api private
*/
function getBasicInfo() {
var result = {};
for (var key in info) {
result[key] = info[key]();
}
return result;
};
info.hostname = os.hostname;
info.type = os.type;
info.platform = os.platform;
info.arch = os.arch;
info.release = os.release;
info.uptime = os.uptime;
info.loadavg = os.loadavg;
info.totalmem = os.totalmem;
info.freemem = os.freemem;
info.cpus = os.cpus;
info.networkInterfaces = os.networkInterfaces;
info.versions = function(){return process.versions};
info.arch = function(){return process.arch};
info.platform = function(){return process.platform};
info.memoryUsage = process.memoryUsage;
info.uptime = process.uptime;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| util.js | 25% | (2 / 8) | 100% | (0 / 0) | 0% | (0 / 1) | 25% | (2 / 8) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 1 1 | /** * convert Date as yyyy-mm-dd hh:mm:ss */ function formatTime(date) { var n = date.getFullYear(); var y = date.getMonth() + 1; var r = date.getDate(); var mytime = date.toLocaleTimeString(); var mytimes = n+ "-" + y + "-" + r + " " + mytime; return mytimes; } module.exports.formatTime = formatTime; |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| codec.js | 14.71% | (5 / 34) | 0% | (0 / 14) | 0% | (0 / 4) | 14.71% | (5 / 34) | |
| constant.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| decoder.js | 20% | (16 / 80) | 0% | (0 / 29) | 0% | (0 / 11) | 20% | (16 / 80) | |
| encoder.js | 11.32% | (12 / 106) | 0% | (0 / 61) | 0% | (0 / 8) | 11.43% | (12 / 105) | |
| parser.js | 12% | (3 / 25) | 0% | (0 / 10) | 0% | (0 / 2) | 12% | (3 / 25) | |
| protobuf.js | 38.24% | (13 / 34) | 0% | (0 / 12) | 0% | (0 / 9) | 38.24% | (13 / 34) | |
| util.js | 23.08% | (3 / 13) | 0% | (0 / 13) | 0% | (0 / 2) | 23.08% | (3 / 13) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 1 1 1 1 1 | var Encoder = module.exports;
/**
* [encode an uInt32, return a array of bytes]
* @param {[integer]} num
* @return {[array]}
*/
Encoder.encodeUInt32 = function(num){
var n = parseInt(num);
if(isNaN(n) || n < 0){
console.log(n);
return null;
}
var result = [];
do{
var tmp = n % 128;
var next = Math.floor(n/128);
if(next !== 0){
tmp = tmp + 128;
}
result.push(tmp);
n = next;
} while(n !== 0);
return result;
};
/**
* [encode a sInt32, return a byte array]
* @param {[sInt32]} num The sInt32 need to encode
* @return {[array]} A byte array represent the integer
*/
Encoder.encodeSInt32 = function(num){
var n = parseInt(num);
if(isNaN(n)){
return null;
}
n = n<0?(Math.abs(n)*2-1):n*2;
return Encoder.encodeUInt32(n);
};
Encoder.decodeUInt32 = function(bytes){
var n = 0;
for(var i = 0; i < bytes.length; i++){
var m = parseInt(bytes[i]);
n = n + ((m & 0x7f) * Math.pow(2,(7*i)));
if(m < 128){
return n;
}
}
return n;
};
Encoder.decodeSInt32 = function(bytes){
var n = this.decodeUInt32(bytes);
var flag = ((n%2) === 1)?-1:1;
n = ((n%2 + n)/2)*flag;
return n;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 | 1 | module.exports = {
TYPES : {
uInt32 : 0,
sInt32 : 0,
int32 : 0,
double : 1,
string : 2,
message : 2,
float : 5
}
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var codec = require('./codec');
var util = require('./util');
var Decoder = module.exports;
var buffer;
var offset = 0;
Decoder.init = function(protos){
this.protos = protos || {};
};
Decoder.setProtos = function(protos){
if(!!protos){
this.protos = protos;
}
};
Decoder.decode = function(route, buf){
var protos = this.protos[route];
buffer = buf;
offset = 0;
if(!!protos){
return decodeMsg({}, protos, buffer.length);
}
return null;
};
function decodeMsg(msg, protos, length){
while(offset<length){
var head = getHead();
var type = head.type;
var tag = head.tag;
var name = protos.__tags[tag];
switch(protos[name].option){
case 'optional' :
case 'required' :
msg[name] = decodeProp(protos[name].type, protos);
break;
case 'repeated' :
if(!msg[name]){
msg[name] = [];
}
decodeArray(msg[name], protos[name].type, protos);
break;
}
}
return msg;
}
/**
* Test if the given msg is finished
*/
function isFinish(msg, protos){
return (!protos.__tags[peekHead().tag]);
}
/**
* Get property head from protobuf
*/
function getHead(){
var tag = codec.decodeUInt32(getBytes());
return {
type : tag&0x7,
tag : tag>>3
};
}
/**
* Get tag head without move the offset
*/
function peekHead(){
var tag = codec.decodeUInt32(peekBytes());
return {
type : tag&0x7,
tag : tag>>3
};
}
function decodeProp(type, protos){
switch(type){
case 'uInt32':
return codec.decodeUInt32(getBytes());
case 'int32' :
case 'sInt32' :
return codec.decodeSInt32(getBytes());
case 'float' :
var float = buffer.readFloatLE(offset);
offset += 4;
return float;
case 'double' :
var double = buffer.readDoubleLE(offset);
offset += 8;
return double;
case 'string' :
var length = codec.decodeUInt32(getBytes());
var str = buffer.toString('utf8', offset, offset+length);
offset += length;
return str;
default :
var message = protos && (protos.__messages[type] || Decoder.protos['message ' + type]);
if(message){
var length = codec.decodeUInt32(getBytes());
var msg = {};
decodeMsg(msg, message, offset+length);
return msg;
}
break;
}
}
function decodeArray(array, type, protos){
if(util.isSimpleType(type)){
var length = codec.decodeUInt32(getBytes());
for(var i = 0; i < length; i++){
array.push(decodeProp(type));
}
}else{
array.push(decodeProp(type, protos));
}
}
function getBytes(flag){
var bytes = [];
var pos = offset;
flag = flag || false;
var b;
do{
var b = buffer.readUInt8(pos);
bytes.push(b);
pos++;
}while(b >= 128);
if(!flag){
offset = pos;
}
return bytes;
}
function peekBytes(){
return getBytes(true);
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | 1 1 1 1 1 1 1 1 1 1 1 1 | var codec = require('./codec');
var constant = require('./constant');
var util = require('./util');
var Encoder = module.exports;
Encoder.init = function(protos){
this.protos = protos || {};
};
Encoder.encode = function(route, msg){
if(!route || !msg){
console.warn('Route or msg can not be null! route : %j, msg %j', route, msg);
return null;
}
//Get protos from protos map use the route as key
var protos = this.protos[route];
//Check msg
if(!checkMsg(msg, protos)){
console.warn('check msg failed! msg : %j, proto : %j', msg, protos);
return null;
}
//Set the length of the buffer 2 times bigger to prevent overflow
var length = Buffer.byteLength(JSON.stringify(msg))*2;
//Init buffer and offset
var buffer = new Buffer(length);
var offset = 0;
if(!!protos){
offset = encodeMsg(buffer, offset, protos, msg);
if(offset > 0){
return buffer.slice(0, offset);
}
}
return null;
};
/**
* Check if the msg follow the defination in the protos
*/
function checkMsg(msg, protos){
if(!protos || !msg){
console.warn('no protos or msg exist! msg : %j, protos : %j', msg, protos);
return false;
}
for(var name in protos){
var proto = protos[name];
//All required element must exist
switch(proto.option){
case 'required' :
if(typeof(msg[name]) === 'undefined'){
console.warn('no property exist for required! name: %j, proto: %j, msg: %j', name, proto, msg);
return false;
}
case 'optional' :
if(typeof(msg[name]) !== 'undefined'){
var message = protos.__messages[proto.type] || Encoder.protos['message ' + proto.type];
if(!!message && !checkMsg(msg[name], message)){
console.warn('inner proto error! name: %j, proto: %j, msg: %j', name, proto, msg);
return false;
}
}
break;
case 'repeated' :
//Check nest message in repeated elements
var message = protos.__messages[proto.type] || Encoder.protos['message ' + proto.type];
if(!!msg[name] && !!message){
for(var i = 0; i < msg[name].length; i++){
if(!checkMsg(msg[name][i], message)){
return false;
}
}
}
break;
}
}
return true;
}
function encodeMsg(buffer, offset, protos, msg){
for(var name in msg){
if(!!protos[name]){
var proto = protos[name];
switch(proto.option){
case 'required' :
case 'optional' :
offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
offset = encodeProp(msg[name], proto.type, offset, buffer, protos);
break;
case 'repeated' :
if(!!msg[name] && msg[name].length > 0){
offset = encodeArray(msg[name], proto, offset, buffer, protos);
}
break;
}
}
}
return offset;
}
function encodeProp(value, type, offset, buffer, protos){
var length = 0;
switch(type){
case 'uInt32':
offset = writeBytes(buffer, offset, codec.encodeUInt32(value));
break;
case 'int32' :
case 'sInt32':
offset = writeBytes(buffer, offset, codec.encodeSInt32(value));
break;
case 'float':
buffer.writeFloatLE(value, offset);
offset += 4;
break;
case 'double':
buffer.writeDoubleLE(value, offset);
offset += 8;
break;
case 'string':
length = Buffer.byteLength(value);
//Encode length
offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
//write string
buffer.write(value, offset, length);
offset += length;
break;
default :
var message = protos.__messages[type] || Encoder.protos['message ' + type];
if(!!message){
//Use a tmp buffer to build an internal msg
var tmpBuffer = new Buffer(Buffer.byteLength(JSON.stringify(value))*2);
length = 0;
length = encodeMsg(tmpBuffer, length, message, value);
//Encode length
offset = writeBytes(buffer, offset, codec.encodeUInt32(length));
//contact the object
tmpBuffer.copy(buffer, offset, 0, length);
offset += length;
}
break;
}
return offset;
}
/**
* Encode reapeated properties, simple msg and object are decode differented
*/
function encodeArray(array, proto, offset, buffer, protos){
var i = 0;
if(util.isSimpleType(proto.type)){
offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
offset = writeBytes(buffer, offset, codec.encodeUInt32(array.length));
for(i = 0; i < array.length; i++){
offset = encodeProp(array[i], proto.type, offset, buffer);
}
}else{
for(i = 0; i < array.length; i++){
offset = writeBytes(buffer, offset, encodeTag(proto.type, proto.tag));
offset = encodeProp(array[i], proto.type, offset, buffer, protos);
}
}
return offset;
}
function writeBytes(buffer, offset, bytes){
for(var i = 0; i < bytes.length; i++){
buffer.writeUInt8(bytes[i], offset);
offset++;
}
return offset;
}
function encodeTag(type, tag){
var value = constant.TYPES[type];
if(value === undefined) value = 2;
return codec.encodeUInt32((tag<<3)|value);
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 1 1 1 | var Parser = module.exports;
/**
* [parse the original protos, give the paresed result can be used by protobuf encode/decode.]
* @param {[Object]} protos Original protos, in a js map.
* @return {[Object]} The presed result, a js object represent all the meta data of the given protos.
*/
Parser.parse = function(protos){
var maps = {};
for(var key in protos){
maps[key] = parseObject(protos[key]);
}
return maps;
};
/**
* [parse a single protos, return a object represent the result. The method can be invocked recursively.]
* @param {[Object]} obj The origin proto need to parse.
* @return {[Object]} The parsed result, a js object.
*/
function parseObject(obj){
var proto = {};
var nestProtos = {};
var tags = {};
for(var name in obj){
var tag = obj[name];
var params = name.split(' ');
switch(params[0]){
case 'message':
if(params.length !== 2){
continue;
}
nestProtos[params[1]] = parseObject(tag);
continue;
case 'required':
case 'optional':
case 'repeated':{
//params length should be 3 and tag can't be duplicated
if(params.length !== 3 || !!tags[tag]){
continue;
}
proto[params[2]] = {
option : params[0],
type : params[1],
tag : tag
};
tags[tag] = params[2];
}
}
}
proto.__messages = nestProtos;
proto.__tags = tags;
return proto;
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 1 1 1 1 1 1 1 1 1 1 1 1 1 | var encoder = require('./encoder');
var decoder = require('./decoder');
var parser = require('./parser');
var Protobuf = module.exports;
/**
* [encode the given message, return a Buffer represent the message encoded by protobuf]
* @param {[type]} key The key to identify the message type.
* @param {[type]} msg The message body, a js object.
* @return {[type]} The binary encode result in a Buffer.
*/
Protobuf.encode = function(key, msg){
return encoder.encode(key, msg);
};
Protobuf.encode2Bytes = function(key, msg){
var buffer = this.encode(key, msg);
if(!buffer || !buffer.length){
console.warn('encode msg failed! key : %j, msg : %j', key, msg);
return null;
}
var bytes = new Uint8Array(buffer.length);
for(var offset = 0; offset < buffer.length; offset++){
bytes[offset] = buffer.readUInt8(offset);
}
return bytes;
};
Protobuf.encodeStr = function(key, msg, code){
code = code || 'base64';
var buffer = Protobuf.encode(key, msg);
return !!buffer?buffer.toString(code):buffer;
};
Protobuf.decode = function(key, msg){
return decoder.decode(key, msg);
};
Protobuf.decodeStr = function(key, str, code){
code = code || 'base64';
var buffer = new Buffer(str, code);
return !!buffer?Protobuf.decode(key, buffer):buffer;
};
Protobuf.parse = function(json){
return parser.parse(json);
};
Protobuf.setEncoderProtos = function(protos){
encoder.init(protos);
};
Protobuf.setDecoderProtos = function(protos){
decoder.init(protos);
};
Protobuf.init = function(opts){
//On the serverside, use serverProtos to encode messages send to client
encoder.init(opts.encoderProtos);
//On the serverside, user clientProtos to decode messages receive from clients
decoder.init(opts.decoderProtos);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 1 1 1 | var util = module.exports; util.isSimpleType = function(type){ return ( type === 'uInt32' || type === 'sInt32' || type === 'int32' || type === 'uInt64' || type === 'sInt64' || type === 'float' || type === 'double'); }; util.equal = function(obj0, obj1){ for(var key in obj0){ var m = obj0[key]; var n = obj1[key]; if(typeof(m) === 'object'){ if(!util.equal(m, n)){ return false; } }else if(m !== n){ return false; } } return true; }; |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) |
| 1 2 | 1 | module.exports = require('./lib/protocol');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| protocol.js | 20.21% | (39 / 193) | 3.45% | (3 / 87) | 6.67% | (1 / 15) | 20.21% | (39 / 193) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | (function (exports, ByteArray, global) {
var Protocol = exports;
var PKG_HEAD_BYTES = 4;
var MSG_FLAG_BYTES = 1;
var MSG_ROUTE_CODE_BYTES = 2;
var MSG_ID_MAX_BYTES = 5;
var MSG_ROUTE_LEN_BYTES = 1;
var MSG_ROUTE_CODE_MAX = 0xffff;
var MSG_COMPRESS_ROUTE_MASK = 0x1;
var MSG_COMPRESS_GZIP_MASK = 0x1;
var MSG_COMPRESS_GZIP_ENCODE_MASK = 1 << 4;
var MSG_TYPE_MASK = 0x7;
var Package = Protocol.Package = {};
var Message = Protocol.Message = {};
Package.TYPE_HANDSHAKE = 1;
Package.TYPE_HANDSHAKE_ACK = 2;
Package.TYPE_HEARTBEAT = 3;
Package.TYPE_DATA = 4;
Package.TYPE_KICK = 5;
Message.TYPE_REQUEST = 0;
Message.TYPE_NOTIFY = 1;
Message.TYPE_RESPONSE = 2;
Message.TYPE_PUSH = 3;
/**
* pomele client encode
* id message id;
* route message route
* msg message body
* socketio current support string
*/
Protocol.strencode = function(str) {
if(typeof Buffer !== "undefined" && ByteArray === Buffer) {
// encoding defaults to 'utf8'
return (new Buffer(str));
} else {
var byteArray = new ByteArray(str.length * 3);
var offset = 0;
for(var i = 0; i < str.length; i++){
var charCode = str.charCodeAt(i);
var codes = null;
if(charCode <= 0x7f){
codes = [charCode];
}else if(charCode <= 0x7ff){
codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
}else{
codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
}
for(var j = 0; j < codes.length; j++){
byteArray[offset] = codes[j];
++offset;
}
}
var _buffer = new ByteArray(offset);
copyArray(_buffer, 0, byteArray, 0, offset);
return _buffer;
}
};
/**
* client decode
* msg String data
* return Message Object
*/
Protocol.strdecode = function(buffer) {
if(typeof Buffer !== "undefined" && ByteArray === Buffer) {
// encoding defaults to 'utf8'
return buffer.toString();
} else {
var bytes = new ByteArray(buffer);
var array = [];
var offset = 0;
var charCode = 0;
var end = bytes.length;
while(offset < end){
if(bytes[offset] < 128){
charCode = bytes[offset];
offset += 1;
}else if(bytes[offset] < 224){
charCode = ((bytes[offset] & 0x1f)<<6) + (bytes[offset+1] & 0x3f);
offset += 2;
}else{
charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
offset += 3;
}
array.push(charCode);
}
return String.fromCharCode.apply(null, array);
}
};
/**
* Package protocol encode.
*
* Pomelo package format:
* +------+-------------+------------------+
* | type | body length | body |
* +------+-------------+------------------+
*
* Head: 4bytes
* 0: package type,
* 1 - handshake,
* 2 - handshake ack,
* 3 - heartbeat,
* 4 - data
* 5 - kick
* 1 - 3: big-endian body length
* Body: body length bytes
*
* @param {Number} type package type
* @param {ByteArray} body body content in bytes
* @return {ByteArray} new byte array that contains encode result
*/
Package.encode = function(type, body){
var length = body ? body.length : 0;
var buffer = new ByteArray(PKG_HEAD_BYTES + length);
var index = 0;
buffer[index++] = type & 0xff;
buffer[index++] = (length >> 16) & 0xff;
buffer[index++] = (length >> 8) & 0xff;
buffer[index++] = length & 0xff;
if(body) {
copyArray(buffer, index, body, 0, length);
}
return buffer;
};
/**
* Package protocol decode.
* See encode for package format.
*
* @param {ByteArray} buffer byte array containing package content
* @return {Object} {type: package type, buffer: body byte array}
*/
Package.decode = function(buffer){
var offset = 0;
var bytes = new ByteArray(buffer);
var length = 0;
var rs = [];
while(offset < bytes.length) {
var type = bytes[offset++];
length = ((bytes[offset++]) << 16 | (bytes[offset++]) << 8 | bytes[offset++]) >>> 0;
var body = length ? new ByteArray(length) : null;
if(body) {
copyArray(body, 0, bytes, offset, length);
}
offset += length;
rs.push({'type': type, 'body': body});
}
return rs.length === 1 ? rs[0]: rs;
};
/**
* Message protocol encode.
*
* @param {Number} id message id
* @param {Number} type message type
* @param {Number} compressRoute whether compress route
* @param {Number|String} route route code or route string
* @param {Buffer} msg message body bytes
* @return {Buffer} encode result
*/
Message.encode = function(id, type, compressRoute, route, msg, compressGzip){
// caculate message max length
var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
var msgLen = MSG_FLAG_BYTES + idBytes;
if(msgHasRoute(type)) {
if(compressRoute) {
if(typeof route !== 'number'){
throw new Error('error flag for number route!');
}
msgLen += MSG_ROUTE_CODE_BYTES;
} else {
msgLen += MSG_ROUTE_LEN_BYTES;
if(route) {
route = Protocol.strencode(route);
if(route.length>255) {
throw new Error('route maxlength is overflow');
}
msgLen += route.length;
}
}
}
if(msg) {
msgLen += msg.length;
}
var buffer = new ByteArray(msgLen);
var offset = 0;
// add flag
offset = encodeMsgFlag(type, compressRoute, buffer, offset, compressGzip);
// add message id
if(msgHasId(type)) {
offset = encodeMsgId(id, buffer, offset);
}
// add route
if(msgHasRoute(type)) {
offset = encodeMsgRoute(compressRoute, route, buffer, offset);
}
// add body
if(msg) {
offset = encodeMsgBody(msg, buffer, offset);
}
return buffer;
};
/**
* Message protocol decode.
*
* @param {Buffer|Uint8Array} buffer message bytes
* @return {Object} message object
*/
Message.decode = function(buffer) {
var bytes = new ByteArray(buffer);
var bytesLen = bytes.length || bytes.byteLength;
var offset = 0;
var id = 0;
var route = null;
// parse flag
var flag = bytes[offset++];
var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
var type = (flag >> 1) & MSG_TYPE_MASK;
var compressGzip = (flag >> 4) & MSG_COMPRESS_GZIP_MASK;
// parse id
if(msgHasId(type)) {
var m = 0;
var i = 0;
do{
m = parseInt(bytes[offset]);
id += (m & 0x7f) << (7 * i);
offset++;
i++;
}while(m >= 128);
}
// parse route
if(msgHasRoute(type)) {
if(compressRoute) {
route = (bytes[offset++]) << 8 | bytes[offset++];
} else {
var routeLen = bytes[offset++];
if(routeLen) {
route = new ByteArray(routeLen);
copyArray(route, 0, bytes, offset, routeLen);
route = Protocol.strdecode(route);
} else {
route = '';
}
offset += routeLen;
}
}
// parse body
var bodyLen = bytesLen - offset;
var body = new ByteArray(bodyLen);
copyArray(body, 0, bytes, offset, bodyLen);
return {'id': id, 'type': type, 'compressRoute': compressRoute,
'route': route, 'body': body, 'compressGzip': compressGzip};
};
var copyArray = function(dest, doffset, src, soffset, length) {
if('function' === typeof src.copy) {
// Buffer
src.copy(dest, doffset, soffset, soffset + length);
} else {
// Uint8Array
for(var index=0; index<length; index++){
dest[doffset++] = src[soffset++];
}
}
};
var msgHasId = function(type) {
return type === Message.TYPE_REQUEST || type === Message.TYPE_RESPONSE;
};
var msgHasRoute = function(type) {
return type === Message.TYPE_REQUEST || type === Message.TYPE_NOTIFY ||
type === Message.TYPE_PUSH;
};
var caculateMsgIdBytes = function(id) {
var len = 0;
do {
len += 1;
id >>= 7;
} while(id > 0);
return len;
};
var encodeMsgFlag = function(type, compressRoute, buffer, offset, compressGzip) {
if(type !== Message.TYPE_REQUEST && type !== Message.TYPE_NOTIFY &&
type !== Message.TYPE_RESPONSE && type !== Message.TYPE_PUSH) {
throw new Error('unkonw message type: ' + type);
}
buffer[offset] = (type << 1) | (compressRoute ? 1 : 0);
if(compressGzip) {
buffer[offset] = buffer[offset] | MSG_COMPRESS_GZIP_ENCODE_MASK;
}
return offset + MSG_FLAG_BYTES;
};
var encodeMsgId = function(id, buffer, offset) {
do{
var tmp = id % 128;
var next = Math.floor(id/128);
if(next !== 0){
tmp = tmp + 128;
}
buffer[offset++] = tmp;
id = next;
} while(id !== 0);
return offset;
};
var encodeMsgRoute = function(compressRoute, route, buffer, offset) {
if (compressRoute) {
if(route > MSG_ROUTE_CODE_MAX){
throw new Error('route number is overflow');
}
buffer[offset++] = (route >> 8) & 0xff;
buffer[offset++] = route & 0xff;
} else {
if(route) {
buffer[offset++] = route.length & 0xff;
copyArray(buffer, offset, route, 0, route.length);
offset += route.length;
} else {
buffer[offset++] = 0;
}
}
return offset;
};
var encodeMsgBody = function(msg, buffer, offset) {
copyArray(buffer, offset, msg, 0, msg.length);
return offset + msg.length;
};
module.exports = Protocol;
Iif(typeof(window) != "undefined") {
window.Protocol = Protocol;
}
})(typeof(window)=="undefined" ? module.exports : (this.Protocol = {}),typeof(window)=="undefined" ? Buffer : Uint8Array, this);
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| index.js | 60% | (3 / 5) | 50% | (1 / 2) | 100% | (0 / 0) | 60% | (3 / 5) |
| 1 2 3 4 5 6 7 8 | 1 1 1 | Iif(process.env.POMELO_RPC_COV) { module.exports.client = require('./lib-cov/rpc-client/client'); module.exports.server = require('./lib-cov/rpc-server/server'); } else { module.exports.client = require('./lib/rpc-client/client'); module.exports.server = require('./lib/rpc-server/server'); } |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| client.js | 22.29% | (37 / 166) | 0% | (0 / 59) | 0% | (0 / 28) | 22.29% | (37 / 166) | |
| failureProcess.js | 13.56% | (8 / 59) | 0% | (0 / 37) | 0% | (0 / 7) | 13.56% | (8 / 59) | |
| mailbox.js | 66.67% | (2 / 3) | 100% | (0 / 0) | 0% | (0 / 1) | 66.67% | (2 / 3) | |
| mailstation.js | 14.1% | (33 / 234) | 0% | (0 / 142) | 0% | (0 / 29) | 14.1% | (33 / 234) | |
| router.js | 8.85% | (10 / 113) | 0% | (0 / 66) | 0% | (0 / 7) | 8.85% | (10 / 113) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'rpc-client');
var failureProcess = require('./failureProcess');
var constants = require('../util/constants');
var Station = require('./mailstation');
var Tracer = require('../util/tracer');
var Loader = require('pomelo-loader');
var utils = require('../util/utils');
var Proxy = require('../util/proxy');
var router = require('./router');
var async = require('async');
/**
* Client states
*/
var STATE_INITED = 1; // client has inited
var STATE_STARTED = 2; // client has started
var STATE_CLOSED = 3; // client has closed
/**
* RPC Client Class
*/
var Client = function(opts) {
opts = opts || {};
this._context = opts.context;
this._routeContext = opts.routeContext;
this.router = opts.router || router.df;
this.routerType = opts.routerType;
this.rpcDebugLog = opts.rpcDebugLog;
if (this._context) {
opts.clientId = this._context.serverId;
}
this.opts = opts;
this.proxies = {};
this._station = createStation(opts);
this.state = STATE_INITED;
};
var pro = Client.prototype;
/**
* Start the rpc client which would try to connect the remote servers and
* report the result by cb.
*
* @param cb {Function} cb(err)
*/
pro.start = function(cb) {
if (this.state > STATE_INITED) {
cb(new Error('rpc client has started.'));
return;
}
var self = this;
this._station.start(function(err) {
if (err) {
logger.error('[pomelo-rpc] client start fail for ' + err.stack);
return cb(err);
}
self._station.on('error', failureProcess.bind(self._station));
self.state = STATE_STARTED;
cb();
});
};
/**
* Stop the rpc client.
*
* @param {Boolean} force
* @return {Void}
*/
pro.stop = function(force) {
if (this.state !== STATE_STARTED) {
logger.warn('[pomelo-rpc] client is not running now.');
return;
}
this.state = STATE_CLOSED;
this._station.stop(force);
};
/**
* Add a new proxy to the rpc client which would overrid the proxy under the
* same key.
*
* @param {Object} record proxy description record, format:
* {namespace, serverType, path}
*/
pro.addProxy = function(record) {
if (!record) {
return;
}
var proxy = generateProxy(this, record, this._context);
if (!proxy) {
return;
}
insertProxy(this.proxies, record.namespace, record.serverType, proxy);
};
/**
* Batch version for addProxy.
*
* @param {Array} records list of proxy description record
*/
pro.addProxies = function(records) {
if (!records || !records.length) {
return;
}
for (var i = 0, l = records.length; i < l; i++) {
this.addProxy(records[i]);
}
};
/**
* Add new remote server to the rpc client.
*
* @param {Object} server new server information
*/
pro.addServer = function(server) {
this._station.addServer(server);
};
/**
* Batch version for add new remote server.
*
* @param {Array} servers server info list
*/
pro.addServers = function(servers) {
this._station.addServers(servers);
};
/**
* Remove remote server from the rpc client.
*
* @param {String|Number} id server id
*/
pro.removeServer = function(id) {
this._station.removeServer(id);
};
/**
* Batch version for remove remote server.
*
* @param {Array} ids remote server id list
*/
pro.removeServers = function(ids) {
this._station.removeServers(ids);
};
/**
* Replace remote servers.
*
* @param {Array} servers server info list
*/
pro.replaceServers = function(servers) {
this._station.replaceServers(servers);
};
/**
* Do the rpc invoke directly.
*
* @param serverId {String} remote server id
* @param msg {Object} rpc message. Message format:
* {serverType: serverType, service: serviceName, method: methodName, args: arguments}
* @param cb {Function} cb(err, ...)
*/
pro.rpcInvoke = function(serverId, msg, cb) {
var rpcDebugLog = this.rpcDebugLog;
var tracer = null;
if (rpcDebugLog) {
tracer = new Tracer(this.opts.rpcLogger, this.opts.rpcDebugLog, this.opts.clientId, serverId, msg);
tracer.info('client', __filename, 'rpcInvoke', 'the entrance of rpc invoke');
}
if (this.state !== STATE_STARTED) {
tracer && tracer.error('client', __filename, 'rpcInvoke', 'fail to do rpc invoke for client is not running');
logger.error('[pomelo-rpc] fail to do rpc invoke for client is not running');
cb(new Error('[pomelo-rpc] fail to do rpc invoke for client is not running'));
return;
}
this._station.dispatch(tracer, serverId, msg, this.opts, cb);
};
/**
* Add rpc before filter.
*
* @param filter {Function} rpc before filter function.
*
* @api public
*/
pro.before = function(filter) {
this._station.before(filter);
};
/**
* Add rpc after filter.
*
* @param filter {Function} rpc after filter function.
*
* @api public
*/
pro.after = function(filter) {
this._station.after(filter);
};
/**
* Add rpc filter.
*
* @param filter {Function} rpc filter function.
*
* @api public
*/
pro.filter = function(filter) {
this._station.filter(filter);
};
/**
* Set rpc filter error handler.
*
* @param handler {Function} rpc filter error handler function.
*
* @api public
*/
pro.setErrorHandler = function(handler) {
this._station.handleError = handler;
};
/**
* Create mail station.
*
* @param opts {Object} construct parameters.
*
* @api private
*/
var createStation = function(opts) {
return Station.create(opts);
};
/**
* Generate proxies for remote servers.
*
* @param client {Object} current client instance.
* @param record {Object} proxy reocrd info. {namespace, serverType, path}
* @param context {Object} mailbox init context parameter
*
* @api private
*/
var generateProxy = function(client, record, context) {
if (!record) {
return;
}
var res, name;
var modules = Loader.load(record.path, context);
if (modules) {
res = {};
for (name in modules) {
res[name] = Proxy.create({
service: name,
origin: modules[name],
attach: record,
proxyCB: proxyCB.bind(null, client)
});
}
}
return res;
};
/**
* Generate prxoy for function type field
*
* @param client {Object} current client instance.
* @param serviceName {String} delegated service name.
* @param methodName {String} delegated method name.
* @param args {Object} rpc invoke arguments.
* @param attach {Object} attach parameter pass to proxyCB.
* @param isToSpecifiedServer {boolean} true means rpc route to specified remote server.
*
* @api private
*/
var proxyCB = function(client, serviceName, methodName, args, attach, isToSpecifiedServer) {
if (client.state !== STATE_STARTED) {
logger.error('[pomelo-rpc] fail to invoke rpc proxy for client is not running');
return;
}
if (args.length < 2) {
logger.error('[pomelo-rpc] invalid rpc invoke, arguments length less than 2, namespace: %j, serverType, %j, serviceName: %j, methodName: %j',
attach.namespace, attach.serverType, serviceName, methodName);
return;
}
var routeParam = args.shift();
var cb = args.pop();
var serverType = attach.serverType;
var msg = {
namespace: attach.namespace,
serverType: serverType,
service: serviceName,
method: methodName,
args: args
};
if (isToSpecifiedServer) {
rpcToSpecifiedServer(client, msg, serverType, routeParam, cb);
} else {
getRouteTarget(client, serverType, msg, routeParam, function(err, serverId) {
if (err) {
return cb(err);
}
client.rpcInvoke(serverId, msg, cb);
});
}
};
/**
* Calculate remote target server id for rpc client.
*
* @param client {Object} current client instance.
* @param serverType {String} remote server type.
* @param routeParam {Object} mailbox init context parameter.
* @param cb {Function} return rpc remote target server id.
*
* @api private
*/
var getRouteTarget = function(client, serverType, msg, routeParam, cb) {
if (!!client.routerType) {
var method;
switch (client.routerType) {
case constants.SCHEDULE.ROUNDROBIN:
method = router.rr;
break;
case constants.SCHEDULE.WEIGHT_ROUNDROBIN:
method = router.wrr;
break;
case constants.SCHEDULE.LEAST_ACTIVE:
method = router.la;
break;
case constants.SCHEDULE.CONSISTENT_HASH:
method = router.ch;
break;
default:
method = router.rd;
break;
}
method.call(null, client, serverType, msg, function(err, serverId) {
cb(err, serverId);
});
} else {
var route, target;
if (typeof client.router === 'function') {
route = client.router;
target = null;
} else if (typeof client.router.route === 'function') {
route = client.router.route;
target = client.router;
} else {
logger.error('[pomelo-rpc] invalid route function.');
return;
}
route.call(target, routeParam, msg, client._routeContext, function(err, serverId) {
cb(err, serverId);
});
}
};
/**
* Rpc to specified server id or servers.
*
* @param client {Object} current client instance.
* @param msg {Object} rpc message.
* @param serverType {String} remote server type.
* @param serverId {Object} mailbox init context parameter.
*
* @api private
*/
var rpcToSpecifiedServer = function(client, msg, serverType, serverId, cb) {
if (typeof serverId !== 'string') {
logger.error('[pomelo-rpc] serverId is not a string : %s', serverId);
return;
}
if (serverId === '*') {
var servers = client._routeContext.getServersByType(serverType);
if (!servers) {
logger.error('[pomelo-rpc] serverType %s servers not exist', serverType);
return;
}
async.each(servers, function(server, next) {
var serverId = server['id'];
client.rpcInvoke(serverId, msg, function(err) {
next(err);
});
}, cb);
} else {
client.rpcInvoke(serverId, msg, cb);
}
};
/**
* Add proxy into array.
*
* @param proxies {Object} rpc proxies
* @param namespace {String} rpc namespace sys/user
* @param serverType {String} rpc remote server type
* @param proxy {Object} rpc proxy
*
* @api private
*/
var insertProxy = function(proxies, namespace, serverType, proxy) {
proxies[namespace] = proxies[namespace] || {};
if (proxies[namespace][serverType]) {
for (var attr in proxy) {
proxies[namespace][serverType][attr] = proxy[attr];
}
} else {
proxies[namespace][serverType] = proxy;
}
};
/**
* RPC client factory method.
*
* @param {Object} opts client init parameter.
* opts.context: mail box init parameter,
* opts.router: (optional) rpc message route function, route(routeParam, msg, cb),
* opts.mailBoxFactory: (optional) mail box factory instance.
* @return {Object} client instance.
*/
module.exports.create = function(opts) {
return new Client(opts);
};
// module.exports.WSMailbox = require('./mailboxes/ws-mailbox'); // socket.io
// module.exports.WS2Mailbox = require('./mailboxes/ws2-mailbox'); // ws
module.exports.MQTTMailbox = require('./mailboxes/mqtt-mailbox'); // mqtt
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'failprocess');
var constants = require('../util/constants');
var utils = require('../util/utils');
module.exports = function(code, tracer, serverId, msg, opts) {
var cb = tracer && tracer.cb;
var mode = opts.failMode;
var FAIL_MODE = constants.FAIL_MODE;
var method = failfast;
if (mode == FAIL_MODE.FAILOVER) {
method = failover;
} else if (mode == FAIL_MODE.FAILBACK) {
method = failback;
} else if (mode == FAIL_MODE.FAILFAST) {
}
// switch (mode) {
// case constants.FAIL_MODE.FAILOVER:
// method = failover;
// break;
// case constants.FAIL_MODE.FAILBACK:
// method = failback;
// break;
// case constants.FAIL_MODE.FAILFAST:
// method = failfast;
// break;
// case constants.FAIL_MODE.FAILSAFE:
// default:
// method = failfast;
// break;
// }
method.call(this, code, tracer, serverId, msg, opts, cb);
};
/**
* Failover rpc failure process. This will try other servers with option retries.
*
* @param code {Number} error code number.
* @param tracer {Object} current rpc tracer.
* @param serverId {String} rpc remote target server id.
* @param msg {Object} rpc message.
* @param opts {Object} rpc client options.
* @param cb {Function} user rpc callback.
*
* @api private
*/
var failover = function(code, tracer, serverId, msg, opts, cb) {
var servers;
var self = this;
var counter = 0;
var success = true;
var serverType = msg.serverType;
if (!tracer || !tracer.servers) {
servers = self.serversMap[serverType];
} else {
servers = tracer.servers;
}
var index = servers.indexOf(serverId);
if (index >= 0) {
servers.splice(index, 1);
}
tracer && (tracer.servers = servers);
if (!servers.length) {
logger.error('[pomelo-rpc] rpc failed with all this type of servers, with serverType: %s', serverType);
cb(new Error('rpc failed with all this type of servers, with serverType: ' + serverType));
return;
}
self.dispatch.call(self, tracer, servers[0], msg, opts, cb);
};
/**
* Failsafe rpc failure process.
*
* @param code {Number} error code number.
* @param tracer {Object} current rpc tracer.
* @param serverId {String} rpc remote target server id.
* @param msg {Object} rpc message.
* @param opts {Object} rpc client options.
* @param cb {Function} user rpc callback.
*
* @api private
*/
var failsafe = function(code, tracer, serverId, msg, opts, cb) {
var self = this;
var retryTimes = opts.retryTimes || constants.DEFAULT_PARAM.FAILSAFE_RETRIES;
var retryConnectTime = opts.retryConnectTime || constants.DEFAULT_PARAM.FAILSAFE_CONNECT_TIME;
if (!tracer.retryTimes) {
tracer.retryTimes = 1;
} else {
tracer.retryTimes += 1;
}
switch (code) {
case constants.RPC_ERROR.SERVER_NOT_STARTED:
case constants.RPC_ERROR.NO_TRAGET_SERVER:
cb(new Error('rpc client is not started or cannot find remote server.'));
break;
case constants.RPC_ERROR.FAIL_CONNECT_SERVER:
if (tracer.retryTimes <= retryTimes) {
setTimeout(function() {
self.connect(tracer, serverId, cb);
}, retryConnectTime * tracer.retryTimes);
} else {
cb(new Error('rpc client failed to connect to remote server: ' + serverId));
}
break;
case constants.RPC_ERROR.FAIL_FIND_MAILBOX:
case constants.RPC_ERROR.FAIL_SEND_MESSAGE:
if (tracer.retryTimes <= retryTimes) {
setTimeout(function() {
self.dispatch.call(self, tracer, serverId, msg, opts, cb);
}, retryConnectTime * tracer.retryTimes);
} else {
cb(new Error('rpc client failed to send message to remote server: ' + serverId));
}
break;
case constants.RPC_ERROR.FILTER_ERROR:
cb(new Error('rpc client filter encounters error.'));
break;
default:
cb(new Error('rpc client unknown error.'));
}
};
/**
* Failback rpc failure process. This will try the same server with sendInterval option and retries option.
*
* @param code {Number} error code number.
* @param tracer {Object} current rpc tracer.
* @param serverId {String} rpc remote target server id.
* @param msg {Object} rpc message.
* @param opts {Object} rpc client options.
* @param cb {Function} user rpc callback.
*
* @api private
*/
var failback = function(code, tracer, serverId, msg, opts, cb) {
// todo record message in background and send the message at timing
};
/**
* Failfast rpc failure process. This will ignore error in rpc client.
*
* @param code {Number} error code number.
* @param tracer {Object} current rpc tracer.
* @param serverId {String} rpc remote target server id.
* @param msg {Object} rpc message.
* @param opts {Object} rpc client options.
* @param cb {Function} user rpc callback.
*
* @api private
*/
var failfast = function(code, tracer, serverId, msg, opts, cb) {
logger.error('rpc failed with error, remote server: %s, msg: %j, error code: %s', serverId, msg, code);
cb && cb(new Error('rpc failed with error code: ' + code));
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 1 1 | /**
* Default mailbox factory
*/
var Mailbox = require('./mailboxes/mqtt-mailbox');
// var Ws2Mailbox = require('./mailboxes/ws2-mailbox');
// var WsMailbox = require('./mailboxes/ws-mailbox');
/**
* default mailbox factory
*
* @param {Object} serverInfo single server instance info, {id, host, port, ...}
* @param {Object} opts construct parameters
* @return {Object} mailbox instancef
*/
module.exports.create = function(serverInfo, opts) {
// var mailbox = opts.mailbox || 'mqtt';
// var Mailbox = null;
// if (mailbox == 'ws') {
// Mailbox = WsMailbox;
// } else if (mailbox == 'ws2') {
// Mailbox = Ws2Mailbox;
// } else if (mailbox == 'mqtt') {
// Mailbox = MqttMailbox;
// }
return Mailbox.create(serverInfo, opts);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'MailStation');
var EventEmitter = require('events').EventEmitter;
var blackhole = require('./mailboxes/blackhole');
var defaultMailboxFactory = require('./mailbox');
var constants = require('../util/constants');
var utils = require('../util/utils');
var util = require('util');
var STATE_INITED = 1; // station has inited
var STATE_STARTED = 2; // station has started
var STATE_CLOSED = 3; // station has closed
/**
* Mail station constructor.
*
* @param {Object} opts construct parameters
*/
var MailStation = function(opts) {
EventEmitter.call(this);
this.opts = opts;
this.servers = {}; // remote server info map, key: server id, value: info
this.serversMap = {}; // remote server info map, key: serverType, value: servers array
this.onlines = {}; // remote server online map, key: server id, value: 0/offline 1/online
this.mailboxFactory = opts.mailboxFactory || defaultMailboxFactory;
// filters
this.befores = [];
this.afters = [];
// pending request queues
this.pendings = {};
this.pendingSize = opts.pendingSize || constants.DEFAULT_PARAM.DEFAULT_PENDING_SIZE;
// connecting remote server mailbox map
this.connecting = {};
// working mailbox map
this.mailboxes = {};
this.state = STATE_INITED;
};
util.inherits(MailStation, EventEmitter);
var pro = MailStation.prototype;
/**
* Init and start station. Connect all mailbox to remote servers.
*
* @param {Function} cb(err) callback function
* @return {Void}
*/
pro.start = function(cb) {
if (this.state > STATE_INITED) {
cb(new Error('station has started.'));
return;
}
var self = this;
process.nextTick(function() {
self.state = STATE_STARTED;
cb();
});
};
/**
* Stop station and all its mailboxes
*
* @param {Boolean} force whether stop station forcely
* @return {Void}
*/
pro.stop = function(force) {
if (this.state !== STATE_STARTED) {
logger.warn('[pomelo-rpc] client is not running now.');
return;
}
this.state = STATE_CLOSED;
var self = this;
function closeAll() {
for (var id in self.mailboxes) {
self.mailboxes[id].close();
}
}
if (force) {
closeAll();
} else {
setTimeout(closeAll, constants.DEFAULT_PARAM.GRACE_TIMEOUT);
}
};
/**
* Add a new server info into the mail station and clear
* the blackhole associated with the server id if any before.
*
* @param {Object} serverInfo server info such as {id, host, port}
*/
pro.addServer = function(serverInfo) {
if (!serverInfo || !serverInfo.id) {
return;
}
var id = serverInfo.id;
var type = serverInfo.serverType;
this.servers[id] = serverInfo;
this.onlines[id] = 1;
if (!this.serversMap[type]) {
this.serversMap[type] = [];
}
if (this.serversMap[type].indexOf(id) < 0) {
this.serversMap[type].push(id);
}
this.emit('addServer', id);
};
/**
* Batch version for add new server info.
*
* @param {Array} serverInfos server info list
*/
pro.addServers = function(serverInfos) {
if (!serverInfos || !serverInfos.length) {
return;
}
for (var i = 0, l = serverInfos.length; i < l; i++) {
this.addServer(serverInfos[i]);
}
};
/**
* Remove a server info from the mail station and remove
* the mailbox instance associated with the server id.
*
* @param {String|Number} id server id
*/
pro.removeServer = function(id) {
this.onlines[id] = 0;
var mailbox = this.mailboxes[id];
if (mailbox) {
mailbox.close();
delete this.mailboxes[id];
}
this.emit('removeServer', id);
};
/**
* Batch version for remove remote servers.
*
* @param {Array} ids server id list
*/
pro.removeServers = function(ids) {
if (!ids || !ids.length) {
return;
}
for (var i = 0, l = ids.length; i < l; i++) {
this.removeServer(ids[i]);
}
};
/**
* Clear station infomation.
*
*/
pro.clearStation = function() {
this.onlines = {};
this.serversMap = {};
}
/**
* Replace remote servers info.
*
* @param {Array} serverInfos server info list
*/
pro.replaceServers = function(serverInfos) {
this.clearStation();
if (!serverInfos || !serverInfos.length) {
return;
}
for (var i = 0, l = serverInfos.length; i < l; i++) {
var id = serverInfos[i].id;
var type = serverInfos[i].serverType;
this.onlines[id] = 1;
if (!this.serversMap[type]) {
this.serversMap[type] = [];
}
this.servers[id] = serverInfos[i];
if (this.serversMap[type].indexOf(id) < 0) {
this.serversMap[type].push(id);
}
}
};
/**
* Dispatch rpc message to the mailbox
*
* @param {Object} tracer rpc debug tracer
* @param {String} serverId remote server id
* @param {Object} msg rpc invoke message
* @param {Object} opts rpc invoke option args
* @param {Function} cb callback function
* @return {Void}
*/
pro.dispatch = function(tracer, serverId, msg, opts, cb) {
tracer && tracer.info('client', __filename, 'dispatch', 'dispatch rpc message to the mailbox');
tracer && (tracer.cb = cb);
if (this.state !== STATE_STARTED) {
tracer && tracer.error('client', __filename, 'dispatch', 'client is not running now');
logger.error('[pomelo-rpc] client is not running now.');
this.emit('error', constants.RPC_ERROR.SERVER_NOT_STARTED, tracer, serverId, msg, opts);
return;
}
var self = this;
var mailbox = this.mailboxes[serverId];
if (!mailbox) {
tracer && tracer.debug('client', __filename, 'dispatch', 'mailbox is not exist');
// try to connect remote server if mailbox instance not exist yet
if (!lazyConnect(tracer, this, serverId, this.mailboxFactory, cb)) {
tracer && tracer.error('client', __filename, 'dispatch', 'fail to find remote server:' + serverId);
logger.error('[pomelo-rpc] fail to find remote server:' + serverId);
self.emit('error', constants.RPC_ERROR.NO_TRAGET_SERVER, tracer, serverId, msg, opts);
}
// push request to the pending queue
addToPending(tracer, this, serverId, arguments);
return;
}
if (this.connecting[serverId]) {
tracer && tracer.debug('client', __filename, 'dispatch', 'request add to connecting');
// if the mailbox is connecting to remote server
addToPending(tracer, this, serverId, arguments);
return;
}
var send = function(tracer, err, serverId, msg, opts) {
tracer && tracer.info('client', __filename, 'send', 'get corresponding mailbox and try to send message');
var mailbox = self.mailboxes[serverId];
if (err) {
return errorHandler(tracer, self, err, serverId, msg, opts, true, cb);
}
if (!mailbox) {
tracer && tracer.error('client', __filename, 'send', 'can not find mailbox with id:' + serverId);
logger.error('[pomelo-rpc] could not find mailbox with id:' + serverId);
self.emit('error', constants.RPC_ERROR.FAIL_FIND_MAILBOX, tracer, serverId, msg, opts);
return;
}
mailbox.send(tracer, msg, opts, function(tracer_send, send_err, args) {
// var tracer_send = arguments[0];
// var send_err = arguments[1];
if (send_err) {
logger.error('[pomelo-rpc] fail to send message %s', send_err.stack || send_err.message);
self.emit('error', constants.RPC_ERROR.FAIL_SEND_MESSAGE, tracer, serverId, msg, opts);
cb && cb(send_err);
// utils.applyCallback(cb, send_err);
return;
}
// var args = arguments[2];
doFilter(tracer_send, null, serverId, msg, opts, self.afters, 0, 'after', function(tracer, err, serverId, msg, opts) {
if (err) {
errorHandler(tracer, self, err, serverId, msg, opts, false, cb);
}
utils.applyCallback(cb, args);
});
});
};
doFilter(tracer, null, serverId, msg, opts, this.befores, 0, 'before', send);
};
/**
* Add a before filter
*
* @param {[type]} filter [description]
* @return {[type]} [description]
*/
pro.before = function(filter) {
if (Array.isArray(filter)) {
this.befores = this.befores.concat(filter);
return;
}
this.befores.push(filter);
};
/**
* Add after filter
*
* @param {[type]} filter [description]
* @return {[type]} [description]
*/
pro.after = function(filter) {
if (Array.isArray(filter)) {
this.afters = this.afters.concat(filter);
return;
}
this.afters.push(filter);
};
/**
* Add before and after filter
*
* @param {[type]} filter [description]
* @return {[type]} [description]
*/
pro.filter = function(filter) {
this.befores.push(filter);
this.afters.push(filter);
};
/**
* Try to connect to remote server
*
* @param {Object} tracer rpc debug tracer
* @return {String} serverId remote server id
* @param {Function} cb callback function
*/
pro.connect = function(tracer, serverId, cb) {
var self = this;
var mailbox = self.mailboxes[serverId];
mailbox.connect(tracer, function(err) {
if (!!err) {
tracer && tracer.error('client', __filename, 'lazyConnect', 'fail to connect to remote server: ' + serverId);
logger.error('[pomelo-rpc] mailbox fail to connect to remote server: ' + serverId);
if (!!self.mailboxes[serverId]) {
delete self.mailboxes[serverId];
}
self.emit('error', constants.RPC_ERROR.FAIL_CONNECT_SERVER, tracer, serverId, null, self.opts);
return;
}
mailbox.on('close', function(id) {
var mbox = self.mailboxes[id];
if (!!mbox) {
mbox.close();
delete self.mailboxes[id];
}
self.emit('close', id);
});
delete self.connecting[serverId];
flushPending(tracer, self, serverId);
});
};
/**
* Do before or after filter
*/
var doFilter = function(tracer, err, serverId, msg, opts, filters, index, operate, cb) {
if (index < filters.length) {
tracer && tracer.info('client', __filename, 'doFilter', 'do ' + operate + ' filter ' + filters[index].name);
}
if (index >= filters.length || !!err) {
cb(tracer, err, serverId, msg, opts);
return;
}
var self = this;
var filter = filters[index];
if (typeof filter === 'function') {
filter(serverId, msg, opts, function(target, message, options) {
index++;
//compatible for pomelo filter next(err) method
if (utils.getObjectClass(target) === 'Error') {
doFilter(tracer, target, serverId, msg, opts, filters, index, operate, cb);
} else {
doFilter(tracer, null, target || serverId, message || msg, options || opts, filters, index, operate, cb);
}
});
return;
}
if (typeof filter[operate] === 'function') {
filter[operate](serverId, msg, opts, function(target, message, options) {
index++;
if (utils.getObjectClass(target) === 'Error') {
doFilter(tracer, target, serverId, msg, opts, filters, index, operate, cb);
} else {
doFilter(tracer, null, target || serverId, message || msg, options || opts, filters, index, operate, cb);
}
});
return;
}
index++;
doFilter(tracer, err, serverId, msg, opts, filters, index, operate, cb);
};
var lazyConnect = function(tracer, station, serverId, factory, cb) {
tracer && tracer.info('client', __filename, 'lazyConnect', 'create mailbox and try to connect to remote server');
var server = station.servers[serverId];
var online = station.onlines[serverId];
if (!server) {
logger.error('[pomelo-rpc] unknown server: %s', serverId);
return false;
}
if (!online || online !== 1) {
logger.error('[pomelo-rpc] server is not online: %s', serverId);
return false;
}
var mailbox = factory.create(server, station.opts);
station.connecting[serverId] = true;
station.mailboxes[serverId] = mailbox;
station.connect(tracer, serverId, cb);
return true;
};
var addToPending = function(tracer, station, serverId, args) {
tracer && tracer.info('client', __filename, 'addToPending', 'add pending requests to pending queue');
var pending = station.pendings[serverId];
if (!pending) {
pending = station.pendings[serverId] = [];
}
if (pending.length > station.pendingSize) {
tracer && tracer.debug('client', __filename, 'addToPending', 'station pending too much for: ' + serverId);
logger.warn('[pomelo-rpc] station pending too much for: %s', serverId);
return;
}
pending.push(args);
};
var flushPending = function(tracer, station, serverId, cb) {
tracer && tracer.info('client', __filename, 'flushPending', 'flush pending requests to dispatch method');
var pending = station.pendings[serverId];
var mailbox = station.mailboxes[serverId];
if (!pending || !pending.length) {
return;
}
if (!mailbox) {
tracer && tracer.error('client', __filename, 'flushPending', 'fail to flush pending messages for empty mailbox: ' + serverId);
logger.error('[pomelo-rpc] fail to flush pending messages for empty mailbox: ' + serverId);
}
for (var i = 0, l = pending.length; i < l; i++) {
station.dispatch.apply(station, pending[i]);
}
delete station.pendings[serverId];
};
var errorHandler = function(tracer, station, err, serverId, msg, opts, flag, cb) {
if (!!station.handleError) {
station.handleError(err, serverId, msg, opts);
} else {
logger.error('[pomelo-rpc] rpc filter error with serverId: %s, err: %j', serverId, err.stack);
station.emit('error', constants.RPC_ERROR.FILTER_ERROR, tracer, serverId, msg, opts);
}
};
/**
* Mail station factory function.
*
* @param {Object} opts construct paramters
* opts.servers {Object} global server info map. {serverType: [{id, host, port, ...}, ...]}
* opts.mailboxFactory {Function} mailbox factory function
* @return {Object} mail station instance
*/
module.exports.create = function(opts) {
return new MailStation(opts || {});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 1 1 1 1 1 1 1 1 1 1 | var ConsistentHash = require('../util/consistentHash');
var utils = require('../util/utils');
var crc = require('crc');
/**
* Calculate route info and return an appropriate server id.
*
* @param session {Object} session object for current rpc request
* @param msg {Object} rpc message. {serverType, service, method, args, opts}
* @param context {Object} context of client
* @param cb(err, serverId)
*/
var defRoute = function(session, msg, context, cb) {
var list = context.getServersByType(msg.serverType);
if (!list || !list.length) {
cb(new Error('can not find server info for type:' + msg.serverType));
return;
}
var uid = session ? (session.uid || '') : '';
var index = Math.abs(crc.crc32(uid.toString())) % list.length;
cb(null, list[index].id);
};
/**
* Random algorithm for calculating server id.
*
* @param client {Object} rpc client.
* @param serverType {String} rpc target serverType.
* @param msg {Object} rpc message.
* @param cb {Function} cb(err, serverId).
*/
var rdRoute = function(client, serverType, msg, cb) {
var servers = client._station.serversMap[serverType];
if (!servers || !servers.length) {
cb(new Error('rpc servers not exist with serverType: ' + serverType));
return;
}
var index = Math.floor(Math.random() * servers.length);
cb(null, servers[index]);
};
/**
* Round-Robin algorithm for calculating server id.
*
* @param client {Object} rpc client.
* @param serverType {String} rpc target serverType.
* @param msg {Object} rpc message.
* @param cb {Function} cb(err, serverId).
*/
var rrRoute = function(client, serverType, msg, cb) {
var servers = client._station.serversMap[serverType];
if (!servers || !servers.length) {
cb(new Error('rpc servers not exist with serverType: ' + serverType));
return;
}
var index;
if (!client.rrParam) {
client.rrParam = {};
}
if (!!client.rrParam[serverType]) {
index = client.rrParam[serverType];
} else {
index = 0;
}
cb(null, servers[index % servers.length]);
if (index++ === Number.MAX_VALUE) {
index = 0;
}
client.rrParam[serverType] = index;
};
/**
* Weight-Round-Robin algorithm for calculating server id.
*
* @param client {Object} rpc client.
* @param serverType {String} rpc target serverType.
* @param msg {Object} rpc message.
* @param cb {Function} cb(err, serverId).
*/
var wrrRoute = function(client, serverType, msg, cb) {
var servers = client._station.serversMap[serverType];
if (!servers || !servers.length) {
cb(new Error('rpc servers not exist with serverType: ' + serverType));
return;
}
var index, weight;
if (!client.wrrParam) {
client.wrrParam = {};
}
if (!!client.wrrParam[serverType]) {
index = client.wrrParam[serverType].index;
weight = client.wrrParam[serverType].weight;
} else {
index = -1;
weight = 0;
}
var getMaxWeight = function() {
var maxWeight = -1;
for (var i = 0; i < servers.length; i++) {
var server = client._station.servers[servers[i]];
if (!!server.weight && server.weight > maxWeight) {
maxWeight = server.weight;
}
}
return maxWeight;
};
while (true) {
index = (index + 1) % servers.length;
if (index === 0) {
weight = weight - 1;
if (weight <= 0) {
weight = getMaxWeight();
if (weight <= 0) {
cb(new Error('rpc wrr route get invalid weight.'));
return;
}
}
}
var server = client._station.servers[servers[index]];
if (server.weight >= weight) {
client.wrrParam[serverType] = {
index: index,
weight: weight
};
cb(null, server.id);
return;
}
}
};
/**
* Least-Active algorithm for calculating server id.
*
* @param client {Object} rpc client.
* @param serverType {String} rpc target serverType.
* @param msg {Object} rpc message.
* @param cb {Function} cb(err, serverId).
*/
var laRoute = function(client, serverType, msg, cb) {
var servers = client._station.serversMap[serverType];
if (!servers || !servers.length) {
return cb(new Error('rpc servers not exist with serverType: ' + serverType));
}
var actives = [];
if (!client.laParam) {
client.laParam = {};
}
if (!!client.laParam[serverType]) {
for (var j = 0; j < servers.length; j++) {
var count = client.laParam[serverType][servers[j]];
if (!count) {
client.laParam[servers[j]] = count = 0;
}
actives.push(count);
}
} else {
client.laParam[serverType] = {};
for (var i = 0; i < servers.length; i++) {
client.laParam[serverType][servers[i]] = 0;
actives.push(0);
}
}
var rs = [];
var minInvoke = Number.MAX_VALUE;
for (var k = 0; k < actives.length; k++) {
if (actives[k] < minInvoke) {
minInvoke = actives[k];
rs = [];
rs.push(servers[k]);
} else if (actives[k] === minInvoke) {
rs.push(servers[k]);
}
}
var index = Math.floor(Math.random() * rs.length);
var serverId = rs[index];
client.laParam[serverType][serverId] += 1;
cb(null, serverId);
};
/**
* Consistent-Hash algorithm for calculating server id.
*
* @param client {Object} rpc client.
* @param serverType {String} rpc target serverType.
* @param msg {Object} rpc message.
* @param cb {Function} cb(err, serverId).
*/
var chRoute = function(client, serverType, msg, cb) {
var servers = client._station.serversMap[serverType];
if (!servers || !servers.length) {
return cb(new Error('rpc servers not exist with serverType: ' + serverType));
}
var index, con;
if (!client.chParam) {
client.chParam = {};
}
if (!!client.chParam[serverType]) {
con = client.chParam[serverType].consistentHash;
} else {
client.opts.station = client._station;
con = new ConsistentHash(servers, client.opts);
}
var hashFieldIndex = client.opts.hashFieldIndex;
var field = msg.args[hashFieldIndex] || JSON.stringify(msg);
cb(null, con.getNode(field));
client.chParam[serverType] = {
consistentHash: con
};
};
module.exports = {
rr: rrRoute,
wrr: wrrRoute,
la: laRoute,
ch: chRoute,
rd: rdRoute,
df: defRoute
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| blackhole.js | 50% | (7 / 14) | 0% | (0 / 4) | 0% | (0 / 5) | 50% | (7 / 14) | |
| mqtt-mailbox.js | 14.81% | (24 / 162) | 0% | (0 / 60) | 0% | (0 / 23) | 14.81% | (24 / 162) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'blackhole');
var EventEmitter = require('events').EventEmitter;
var utils = require('../../util/utils');
var exp = module.exports = new EventEmitter();
exp.connect = function(tracer, cb) {
tracer && tracer.info('client', __filename, 'connect', 'connect to blackhole');
process.nextTick(function() {
cb(new Error('fail to connect to remote server and switch to blackhole.'));
});
};
exp.close = function(cb) {};
exp.send = function(tracer, msg, opts, cb) {
tracer && tracer.info('client', __filename, 'send', 'send rpc msg to blackhole');
logger.info('message into blackhole: %j', msg);
process.nextTick(function() {
cb(tracer, new Error('message was forward to blackhole.'));
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'mqtt-mailbox');
var EventEmitter = require('events').EventEmitter;
var constants = require('../../util/constants');
var Tracer = require('../../util/tracer');
var MqttCon = require('mqtt-connection');
var utils = require('../../util/utils');
var util = require('util');
var net = require('net');
var CONNECT_TIMEOUT = 2000;
var MailBox = function(server, opts) {
EventEmitter.call(this);
this.curId = 0;
this.id = server.id;
this.host = server.host;
this.port = server.port;
this.requests = {};
this.timeout = {};
this.queue = [];
this.bufferMsg = opts.bufferMsg;
this.keepalive = opts.keepalive || constants.DEFAULT_PARAM.KEEPALIVE;
this.interval = opts.interval || constants.DEFAULT_PARAM.INTERVAL;
this.timeoutValue = opts.timeout || constants.DEFAULT_PARAM.CALLBACK_TIMEOUT;
this.keepaliveTimer = null;
this.lastPing = -1;
this.lastPong = -1;
this.connected = false;
this.closed = false;
this.opts = opts;
this.serverId = opts.context.serverId;
};
util.inherits(MailBox, EventEmitter);
MailBox.prototype.connect = function(tracer, cb) {
tracer && tracer.info('client', __filename, 'connect', 'mqtt-mailbox try to connect');
if (this.connected) {
tracer && tracer.error('client', __filename, 'connect', 'mailbox has already connected');
return cb(new Error('mailbox has already connected.'));
}
var self = this;
var stream = net.createConnection(this.port, this.host);
this.socket = MqttCon(stream);
var connectTimeout = setTimeout(function() {
logger.error('rpc client %s connect to remote server %s timeout', self.serverId, self.id);
self.emit('close', self.id);
}, CONNECT_TIMEOUT);
this.socket.connect({
clientId: 'MQTT_RPC_' + Date.now()
}, function() {
if (self.connected) {
return;
}
clearTimeout(connectTimeout);
self.connected = true;
if (self.bufferMsg) {
self._interval = setInterval(function() {
flush(self);
}, self.interval);
}
self.setupKeepAlive();
cb();
});
this.socket.on('publish', function(pkg) {
pkg = pkg.payload.toString();
try {
pkg = JSON.parse(pkg);
if (pkg instanceof Array) {
processMsgs(self, pkg);
} else {
processMsg(self, pkg);
}
} catch (err) {
logger.error('rpc client %s process remote server %s message with error: %s', self.serverId, self.id, err.stack);
}
});
this.socket.on('error', function(err) {
logger.error('rpc socket %s is error, remote server %s host: %s, port: %s', self.serverId, self.id, self.host, self.port);
self.emit('close', self.id);
});
this.socket.on('pingresp', function() {
self.lastPong = Date.now();
});
this.socket.on('disconnect', function(reason) {
logger.error('rpc socket %s is disconnect from remote server %s, reason: %s', self.serverId, self.id, reason);
var reqs = self.requests;
for (var id in reqs) {
var ReqCb = reqs[id];
ReqCb(tracer, new Error(self.serverId + ' disconnect with remote server ' + self.id));
}
self.emit('close', self.id);
});
};
/**
* close mailbox
*/
MailBox.prototype.close = function() {
if (this.closed) {
return;
}
this.closed = true;
this.connected = false;
if (this._interval) {
clearInterval(this._interval);
this._interval = null;
}
this.socket.destroy();
};
/**
* send message to remote server
*
* @param msg {service:"", method:"", args:[]}
* @param opts {} attach info to send method
* @param cb declaration decided by remote interface
*/
MailBox.prototype.send = function(tracer, msg, opts, cb) {
tracer && tracer.info('client', __filename, 'send', 'mqtt-mailbox try to send');
if (!this.connected) {
tracer && tracer.error('client', __filename, 'send', 'mqtt-mailbox not init');
cb(tracer, new Error(this.serverId + ' mqtt-mailbox is not init ' + this.id));
return;
}
if (this.closed) {
tracer && tracer.error('client', __filename, 'send', 'mailbox has already closed');
cb(tracer, new Error(this.serverId + ' mqtt-mailbox has already closed ' + this.id));
return;
}
var id = this.curId++;
this.requests[id] = cb;
setCbTimeout(this, id, tracer, cb);
var pkg;
if (tracer && tracer.isEnabled) {
pkg = {
traceId: tracer.id,
seqId: tracer.seq,
source: tracer.source,
remote: tracer.remote,
id: id,
msg: msg
};
} else {
pkg = {
id: id,
msg: msg
};
}
if (this.bufferMsg) {
enqueue(this, pkg);
} else {
doSend(this.socket, pkg);
}
};
MailBox.prototype.setupKeepAlive = function() {
var self = this;
this.keepaliveTimer = setInterval(function() {
self.checkKeepAlive();
}, this.keepalive);
}
MailBox.prototype.checkKeepAlive = function() {
if (this.closed) {
return;
}
// console.log('checkKeepAlive lastPing %d lastPong %d ~~~', this.lastPing, this.lastPong);
var now = Date.now();
var KEEP_ALIVE_TIMEOUT = this.keepalive * 2;
if (this.lastPing > 0) {
if (this.lastPong < this.lastPing) {
if (now - this.lastPing > KEEP_ALIVE_TIMEOUT) {
logger.error('mqtt rpc client %s checkKeepAlive timeout from remote server %s for %d lastPing: %s lastPong: %s', this.serverId, this.id, KEEP_ALIVE_TIMEOUT, this.lastPing, this.lastPong);
this.emit('close', this.id);
this.lastPing = -1;
// this.close();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
} else {
this.socket.pingreq();
this.lastPing = Date.now();
}
}
var enqueue = function(mailbox, msg) {
mailbox.queue.push(msg);
};
var flush = function(mailbox) {
if (mailbox.closed || !mailbox.queue.length) {
return;
}
doSend(mailbox.socket, mailbox.queue);
mailbox.queue = [];
};
var doSend = function(socket, msg) {
socket.publish({
topic: 'rpc',
payload: JSON.stringify(msg)
});
}
var processMsgs = function(mailbox, pkgs) {
for (var i = 0, l = pkgs.length; i < l; i++) {
processMsg(mailbox, pkgs[i]);
}
};
var processMsg = function(mailbox, pkg) {
var pkgId = pkg.id;
clearCbTimeout(mailbox, pkgId);
var cb = mailbox.requests[pkgId];
if (!cb) {
return;
}
delete mailbox.requests[pkgId];
var rpcDebugLog = mailbox.opts.rpcDebugLog;
var tracer = null;
var sendErr = null;
if (rpcDebugLog) {
tracer = new Tracer(mailbox.opts.rpcLogger, mailbox.opts.rpcDebugLog, mailbox.opts.clientId, pkg.source, pkg.resp, pkg.traceId, pkg.seqId);
}
var pkgResp = pkg.resp;
cb(tracer, sendErr, pkgResp);
};
var setCbTimeout = function(mailbox, id, tracer, cb) {
var timer = setTimeout(function() {
// logger.warn('rpc request is timeout, id: %s, host: %s, port: %s', id, mailbox.host, mailbox.port);
clearCbTimeout(mailbox, id);
if (mailbox.requests[id]) {
delete mailbox.requests[id];
}
var eMsg = util.format('rpc %s callback timeout %d, remote server %s host: %s, port: %s', mailbox.serverId, mailbox.timeoutValue, id, mailbox.host, mailbox.port);
logger.error(eMsg);
cb(tracer, new Error(eMsg));
}, mailbox.timeoutValue);
mailbox.timeout[id] = timer;
};
var clearCbTimeout = function(mailbox, id) {
if (!mailbox.timeout[id]) {
logger.warn('timer is not exsits, serverId: %s remote: %s, host: %s, port: %s', mailbox.serverId, id, mailbox.host, mailbox.port);
return;
}
clearTimeout(mailbox.timeout[id]);
delete mailbox.timeout[id];
};
/**
* Factory method to create mailbox
*
* @param {Object} server remote server info {id:"", host:"", port:""}
* @param {Object} opts construct parameters
* opts.bufferMsg {Boolean} msg should be buffered or send immediately.
* opts.interval {Boolean} msg queue flush interval if bufferMsg is true. default is 50 ms
*/
module.exports.create = function(server, opts) {
return new MailBox(server, opts || {});
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| acceptor.js | 66.67% | (2 / 3) | 100% | (0 / 0) | 0% | (0 / 1) | 66.67% | (2 / 3) | |
| dispatcher.js | 25% | (8 / 32) | 0% | (0 / 14) | 0% | (0 / 3) | 25% | (8 / 32) | |
| gateway.js | 26.32% | (15 / 57) | 0% | (0 / 24) | 0% | (0 / 9) | 26.32% | (15 / 57) | |
| server.js | 27.27% | (6 / 22) | 0% | (0 / 10) | 0% | (0 / 3) | 27.27% | (6 / 22) |
| 1 2 3 4 5 6 7 | 1 1 | var acceptor = require('./acceptors/mqtt-acceptor');
// var acceptor = require('./acceptors/ws2-acceptor');
module.exports.create = function(opts, cb) {
return acceptor.create(opts, cb);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 1 1 1 1 1 1 1 1 | var EventEmitter = require('events').EventEmitter;
var utils = require('../util/utils');
var util = require('util');
var Dispatcher = function(services) {
EventEmitter.call(this);
var self = this;
this.on('reload', function(services) {
self.services = services;
});
this.services = services;
};
util.inherits(Dispatcher, EventEmitter);
module.exports = Dispatcher;
var pro = Dispatcher.prototype;
/**
* route the msg to appropriate service object
*
* @param msg msg package {service:serviceString, method:methodString, args:[]}
* @param services services object collection, such as {service1: serviceObj1, service2: serviceObj2}
* @param cb(...) callback function that should be invoked as soon as the rpc finished
*/
pro.route = function(tracer, msg, cb) {
tracer && tracer.info('server', __filename, 'route', 'route messsage to appropriate service object');
var namespace = this.services[msg.namespace];
if (!namespace) {
tracer && tracer.error('server', __filename, 'route', 'no such namespace:' + msg.namespace);
cb(new Error('no such namespace:' + msg.namespace));
return;
}
var service = namespace[msg.service];
if (!service) {
tracer && tracer.error('server', __filename, 'route', 'no such service:' + msg.service);
cb(new Error('no such service:' + msg.service));
return;
}
var method = service[msg.method];
if (!method) {
tracer && tracer.error('server', __filename, 'route', 'no such method:' + msg.method);
cb(new Error('no such method:' + msg.method));
return;
}
var args = msg.args;
args.push(cb);
method.apply(service, args);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var defaultAcceptorFactory = require('./acceptor');
var EventEmitter = require('events').EventEmitter;
var Dispatcher = require('./dispatcher');
var Loader = require('pomelo-loader');
var utils = require('../util/utils');
var util = require('util');
var fs = require('fs');
var Gateway = function(opts) {
EventEmitter.call(this);
this.opts = opts || {};
this.port = opts.port || 3050;
this.started = false;
this.stoped = false;
this.acceptorFactory = opts.acceptorFactory || defaultAcceptorFactory;
this.services = opts.services;
var dispatcher = new Dispatcher(this.services);
if (!!this.opts.reloadRemotes) {
watchServices(this, dispatcher);
}
this.acceptor = this.acceptorFactory.create(opts, function(tracer, msg, cb) {
dispatcher.route(tracer, msg, cb);
});
};
util.inherits(Gateway, EventEmitter);
var pro = Gateway.prototype;
pro.stop = function() {
if (!this.started || this.stoped) {
return;
}
this.stoped = true;
try {
this.acceptor.close();
} catch (err) {}
};
pro.start = function() {
if (this.started) {
throw new Error('gateway already start.');
}
this.started = true;
var self = this;
this.acceptor.on('error', self.emit.bind(self, 'error'));
this.acceptor.on('closed', self.emit.bind(self, 'closed'));
this.acceptor.listen(this.port);
};
/**
* create and init gateway
*
* @param opts {services: {rpcServices}, connector:conFactory(optional), router:routeFunction(optional)}
*/
module.exports.create = function(opts) {
if (!opts || !opts.services) {
throw new Error('opts and opts.services should not be empty.');
}
return new Gateway(opts);
};
var watchServices = function(gateway, dispatcher) {
var paths = gateway.opts.paths;
var app = gateway.opts.context;
for (var i = 0; i < paths.length; i++) {
(function(index) {
fs.watch(paths[index].path, function(event, name) {
if (event === 'change') {
var res = {};
var item = paths[index];
var m = Loader.load(item.path, app);
if (m) {
createNamespace(item.namespace, res);
for (var s in m) {
res[item.namespace][s] = m[s];
}
}
dispatcher.emit('reload', res);
}
});
})(i);
}
};
var createNamespace = function(namespace, proxies) {
proxies[namespace] = proxies[namespace] || {};
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 1 1 1 1 1 1 | var Loader = require('pomelo-loader');
var Gateway = require('./gateway');
var loadRemoteServices = function(paths, context) {
var res = {},
item, m;
for (var i = 0, l = paths.length; i < l; i++) {
item = paths[i];
m = Loader.load(item.path, context);
if (m) {
createNamespace(item.namespace, res);
for (var s in m) {
res[item.namespace][s] = m[s];
}
}
}
return res;
};
var createNamespace = function(namespace, proxies) {
proxies[namespace] = proxies[namespace] || {};
};
/**
* Create rpc server.
*
* @param {Object} opts construct parameters
* opts.port {Number|String} rpc server listen port
* opts.paths {Array} remote service code paths, [{namespace, path}, ...]
* opts.context {Object} context for remote service
* opts.acceptorFactory {Object} (optionals)acceptorFactory.create(opts, cb)
* @return {Object} rpc server instance
*/
module.exports.create = function(opts) {
if (!opts || !opts.port || opts.port < 0 || !opts.paths) {
throw new Error('opts.port or opts.paths invalid.');
}
var services = loadRemoteServices(opts.paths, opts.context);
opts.services = services;
var gateway = Gateway.create(opts);
return gateway;
};
// module.exports.WSAcceptor = require('./acceptors/ws-acceptor');
// module.exports.TcpAcceptor = require('./acceptors/tcp-acceptor');
module.exports.MqttAcceptor = require('./acceptors/mqtt-acceptor');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| mqtt-acceptor.js | 17.07% | (21 / 123) | 0% | (0 / 34) | 0% | (0 / 21) | 17.07% | (21 / 123) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'mqtt-acceptor');
var EventEmitter = require('events').EventEmitter;
var Tracer = require('../../util/tracer');
var utils = require('../../util/utils');
var MqttCon = require('mqtt-connection');
var util = require('util');
var net = require('net');
var curId = 1;
var Acceptor = function(opts, cb) {
EventEmitter.call(this);
this.interval = opts.interval; // flush interval in ms
this.bufferMsg = opts.bufferMsg;
this.rpcLogger = opts.rpcLogger;
this.rpcDebugLog = opts.rpcDebugLog;
this._interval = null; // interval object
this.sockets = {};
this.msgQueues = {};
this.cb = cb;
};
util.inherits(Acceptor, EventEmitter);
var pro = Acceptor.prototype;
pro.listen = function(port) {
//check status
if (!!this.inited) {
this.cb(new Error('already inited.'));
return;
}
this.inited = true;
var self = this;
this.server = new net.Server();
this.server.listen(port);
this.server.on('error', function(err) {
logger.error('rpc server is error: %j', err.stack);
self.emit('error', err);
});
this.server.on('connection', function(stream) {
var socket = MqttCon(stream);
socket['id'] = curId++;
socket.on('connect', function(pkg) {
console.log('connected');
});
socket.on('publish', function(pkg) {
pkg = pkg.payload.toString();
var isArray = false;
try {
pkg = JSON.parse(pkg);
if (pkg instanceof Array) {
processMsgs(socket, self, pkg);
isArray = true;
} else {
processMsg(socket, self, pkg);
}
} catch (err) {
if (!isArray) {
doSend(socket, {
id: pkg.id,
resp: [cloneError(err)]
});
}
logger.error('process rpc message error %s', err.stack);
}
});
socket.on('pingreq', function() {
socket.pingresp();
});
socket.on('error', function() {
self.onSocketClose(socket);
});
socket.on('close', function() {
self.onSocketClose(socket);
});
self.sockets[socket.id] = socket;
socket.on('disconnect', function(reason) {
self.onSocketClose(socket);
});
});
if (this.bufferMsg) {
this._interval = setInterval(function() {
flush(self);
}, this.interval);
}
};
pro.close = function() {
if (this.closed) {
return;
}
this.closed = true;
if (this._interval) {
clearInterval(this._interval);
this._interval = null;
}
this.server.close();
this.emit('closed');
};
pro.onSocketClose = function(socket) {
if (!socket['closed']) {
var id = socket.id;
socket['closed'] = true;
delete this.sockets[id];
delete this.msgQueues[id];
}
}
var cloneError = function(origin) {
// copy the stack infos for Error instance json result is empty
var res = {
msg: origin.msg,
stack: origin.stack
};
return res;
};
var processMsg = function(socket, acceptor, pkg) {
var tracer = null;
if (this.rpcDebugLog) {
tracer = new Tracer(acceptor.rpcLogger, acceptor.rpcDebugLog, pkg.remote, pkg.source, pkg.msg, pkg.traceId, pkg.seqId);
tracer.info('server', __filename, 'processMsg', 'mqtt-acceptor receive message and try to process message');
}
acceptor.cb(tracer, pkg.msg, function() {
// var args = Array.prototype.slice.call(arguments, 0);
var len = arguments.length;
var args = new Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
var errorArg = args[0]; // first callback argument can be error object, the others are message
if (errorArg && errorArg instanceof Error) {
args[0] = cloneError(errorArg);
}
var resp;
if (tracer && tracer.isEnabled) {
resp = {
traceId: tracer.id,
seqId: tracer.seq,
source: tracer.source,
id: pkg.id,
resp: args
};
} else {
resp = {
id: pkg.id,
resp: args
};
}
if (acceptor.bufferMsg) {
enqueue(socket, acceptor, resp);
} else {
doSend(socket, resp);
}
});
};
var processMsgs = function(socket, acceptor, pkgs) {
for (var i = 0, l = pkgs.length; i < l; i++) {
processMsg(socket, acceptor, pkgs[i]);
}
};
var enqueue = function(socket, acceptor, msg) {
var id = socket.id;
var queue = acceptor.msgQueues[id];
if (!queue) {
queue = acceptor.msgQueues[id] = [];
}
queue.push(msg);
};
var flush = function(acceptor) {
var sockets = acceptor.sockets,
queues = acceptor.msgQueues,
queue, socket;
for (var socketId in queues) {
socket = sockets[socketId];
if (!socket) {
// clear pending messages if the socket not exist any more
delete queues[socketId];
continue;
}
queue = queues[socketId];
if (!queue.length) {
continue;
}
doSend(socket, queue);
queues[socketId] = [];
}
};
var doSend = function(socket, msg) {
socket.publish({
topic: 'rpc',
payload: JSON.stringify(msg)
});
}
/**
* create acceptor
*
* @param opts init params
* @param cb(tracer, msg, cb) callback function that would be invoked when new message arrives
*/
module.exports.create = function(opts, cb) {
return new Acceptor(opts || {}, cb);
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| consistentHash.js | 15.87% | (10 / 63) | 0% | (0 / 28) | 0% | (0 / 8) | 15.87% | (10 / 63) | |
| constants.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| proxy.js | 16.13% | (5 / 31) | 0% | (0 / 10) | 0% | (0 / 6) | 16.13% | (5 / 31) | |
| tracer.js | 24.24% | (8 / 33) | 0% | (0 / 12) | 0% | (0 / 6) | 24.24% | (8 / 33) | |
| utils.js | 26.21% | (27 / 103) | 1.85% | (1 / 54) | 7.69% | (1 / 13) | 26.21% | (27 / 103) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 1 1 1 1 1 1 1 1 1 1 | var crypto = require('crypto');
var ConsistentHash = function(nodes, opts) {
this.opts = opts || {};
this.replicas = this.opts.replicas || 100;
this.algorithm = this.opts.algorithm || 'md5';
this.station = this.opts.station;
this.ring = {};
this.keys = [];
this.nodes = [];
for (var i = 0; i < nodes.length; i++) {
this.addNode(nodes[i]);
}
this.station.on('addServer', this.addNode.bind(this));
this.station.on('removeServer', this.removeNode.bind(this));
};
module.exports = ConsistentHash;
ConsistentHash.prototype.addNode = function(node) {
this.nodes.push(node);
for (var i = 0; i < this.replicas; i++) {
var key = hash(this.algorithm, (node.id || node) + ':' + i);
this.keys.push(key);
this.ring[key] = node;
}
this.keys.sort();
};
ConsistentHash.prototype.removeNode = function(node) {
for (var i = 0; i < this.nodes.length; i++) {
if (this.nodes[i] === node) {
this.nodes.splice(i, 1);
i--;
}
}
for (var j = 0; j < this.replicas; j++) {
var key = hash(this.algorithm, (node.id || node) + ':' + j);
delete this.ring[key];
for (var k = 0; k < this.keys.length; k++) {
if (this.keys[k] === key) {
this.keys.splice(k, 1);
k--;
}
}
}
};
ConsistentHash.prototype.getNode = function(key) {
if (getKeysLength(this.ring) === 0) {
return 0;
}
var result = hash(this.algorithm, key);
var pos = this.getNodePosition(result);
return this.ring[this.keys[pos]];
};
ConsistentHash.prototype.getNodePosition = function(result) {
var upper = getKeysLength(this.ring) - 1;
var lower = 0;
var idx = 0;
var comp = 0;
if (upper === 0) {
return 0;
}
//binary search
while (lower <= upper) {
idx = Math.floor((lower + upper) / 2);
comp = compare(this.keys[idx], result);
if (comp === 0) {
return idx;
} else if (comp > 0) {
upper = idx - 1;
} else {
lower = idx + 1;
}
}
if (upper < 0) {
upper = getKeysLength(this.ring) - 1;
}
return upper;
};
var getKeysLength = function(map) {
return Object.keys(map).length;
};
var hash = function(algorithm, str) {
return crypto.createHash(algorithm).update(str).digest('hex');
};
var compare = function(v1, v2) {
return v1 > v2 ? 1 : v1 < v2 ? -1 : 0;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 1 | module.exports = {
FAIL_MODE: {
FAILOVER: 'failover',
FAILFAST: 'failfast',
FAILSAFE: 'failsafe',
FAILBACK: 'failback'
},
SCHEDULE: {
ROUNDROBIN: 'rr',
WEIGHT_ROUNDROBIN: 'wrr',
LEAST_ACTIVE: 'la',
CONSISTENT_HASH: 'ch'
},
DEFAULT_PARAM: {
FAILSAFE_RETRIES: 3,
FAILSAFE_CONNECT_TIME: 5 * 1000,
CALLBACK_TIMEOUT: 30 * 1000,
INTERVAL: 50,
GRACE_TIMEOUT: 3 * 1000,
DEFAULT_PENDING_SIZE: 10000,
KEEPALIVE: 10 * 1000
},
RPC_ERROR: {
SERVER_NOT_STARTED: 1,
NO_TRAGET_SERVER: 2,
FAIL_CONNECT_SERVER: 3,
FAIL_FIND_MAILBOX: 4,
FAIL_SEND_MESSAGE: 5,
FILTER_ERROR: 6
},
TOPIC_RPC: 'r',
TOPIC_HANDSHAKE: 'h'
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo-rpc', 'rpc-proxy');
var exp = module.exports;
/**
* Create proxy.
*
* @param {Object} opts construct parameters
* opts.origin {Object} delegated object
* opts.proxyCB {Function} proxy invoke callback
* opts.service {String} deletgated service name
* opts.attach {Object} attach parameter pass to proxyCB
* @return {Object} proxy instance
*/
exp.create = function(opts) {
if (!opts || !opts.origin) {
logger.warn('opts and opts.origin should not be empty.');
return null;
}
if (!opts.proxyCB || typeof opts.proxyCB !== 'function') {
logger.warn('opts.proxyCB is not a function, return the origin module directly.');
return opts.origin;
}
return genObjectProxy(opts.service, opts.origin, opts.attach, opts.proxyCB);
};
var genObjectProxy = function(serviceName, origin, attach, proxyCB) {
//generate proxy for function field
var res = {};
for (var field in origin) {
if (typeof origin[field] === 'function') {
res[field] = genFunctionProxy(serviceName, field, origin, attach, proxyCB);
}
}
return res;
};
/**
* Generate prxoy for function type field
*
* @param namespace {String} current namespace
* @param serverType {String} server type string
* @param serviceName {String} delegated service name
* @param methodName {String} delegated method name
* @param origin {Object} origin object
* @param proxyCB {Functoin} proxy callback function
* @returns function proxy
*/
var genFunctionProxy = function(serviceName, methodName, origin, attach, proxyCB) {
return (function() {
var proxy = function() {
// var args = arguments;
var len = arguments.length;
var args = new Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
// var args = Array.prototype.slice.call(arguments, 0);
proxyCB(serviceName, methodName, args, attach);
};
proxy.toServer = function() {
// var args = arguments;
var len = arguments.length;
var args = new Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
proxyCB(serviceName, methodName, args, attach, true);
};
return proxy;
})();
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 1 1 1 1 1 1 1 1 | var uuid = require('node-uuid');
var Tracer = function(logger, enabledRpcLog, source, remote, msg, id, seq) {
this.isEnabled = enabledRpcLog;
if (!enabledRpcLog) {
return;
}
this.logger = logger;
this.source = source;
this.remote = remote;
this.id = id || uuid.v1();
this.seq = seq || 1;
this.msg = msg;
};
module.exports = Tracer;
Tracer.prototype.getLogger = function(role, module, method, des) {
return {
traceId: this.id,
seq: this.seq++,
role: role,
source: this.source,
remote: this.remote,
module: getModule(module),
method: method,
args: this.msg,
timestamp: Date.now(),
description: des
};
};
Tracer.prototype.info = function(role, module, method, des) {
if (this.isEnabled) {
this.logger.info(JSON.stringify(this.getLogger(role, module, method, des)));
}
return;
};
Tracer.prototype.debug = function(role, module, method, des) {
if (this.isEnabled) {
this.logger.debug(JSON.stringify(this.getLogger(role, module, method, des)));
}
return;
};
Tracer.prototype.error = function(role, module, method, des) {
if (this.isEnabled) {
this.logger.error(JSON.stringify(this.getLogger(role, module, method, des)));
}
return;
};
var getModule = function(module) {
var rs = '';
var strs = module.split('/');
var lines = strs.slice(-3);
for (var i = 0; i < lines.length; i++) {
rs += '/' + lines[i];
}
return rs;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 10 1 1 1 1 1 | var Utils = {};
Utils.invokeCallback = function(cb) {
if (typeof cb === 'function') {
cb.apply(null, Array.prototype.slice.call(arguments, 1));
}
};
Utils.applyCallback = function(cb, args) {
if (typeof cb === 'function') {
cb.apply(null, args);
}
};
Utils.getObjectClass = function(obj) {
if (!obj) {
return;
}
var constructor = obj.constructor;
if (!constructor) {
return;
}
if (constructor.name) {
return constructor.name;
}
var str = constructor.toString();
if (!str) {
return;
}
var arr = null;
if (str.charAt(0) == '[') {
arr = str.match(/\[\w+\s*(\w+)\]/);
} else {
arr = str.match(/function\s*(\w+)/);
}
if (arr && arr.length == 2) {
return arr[1];
}
};
/**
* Utils check float
*
* @param {Float} float
* @return {Boolean} true|false
* @api public
*/
Utils.checkFloat = function(v) {
return v === Number(v) && v % 1 !== 0;
// return parseInt(v) !== v;
}
/**
* Utils check type
*
* @param {String} type
* @return {Function} high order function
* @api public
*/
Utils.isType = function(type) {
return function(obj) {
return {}.toString.call(obj) == "[object " + type + "]";
}
}
/**
* Utils check array
*
* @param {Array} array
* @return {Boolean} true|false
* @api public
*/
Utils.checkArray = Array.isArray || Utils.isType("Array");
/**
* Utils check number
*
* @param {Number} number
* @return {Boolean} true|false
* @api public
*/
Utils.checkNumber = Utils.isType("Number");
/**
* Utils check function
*
* @param {Function} func function
* @return {Boolean} true|false
* @api public
*/
Utils.checkFunction = Utils.isType("Function");
/**
* Utils check object
*
* @param {Object} obj object
* @return {Boolean} true|false
* @api public
*/
Utils.checkObject = Utils.isType("Object");
/**
* Utils check string
*
* @param {String} string
* @return {Boolean} true|false
* @api public
*/
Utils.checkString = Utils.isType("String");
/**
* Utils check boolean
*
* @param {Object} obj object
* @return {Boolean} true|false
* @api public
*/
Utils.checkBoolean = Utils.isType("Boolean");
/**
* Utils check bean
*
* @param {Object} obj object
* @return {Boolean} true|false
* @api public
*/
Utils.checkBean = function(obj) {
return obj && obj['$id'] &&
Utils.checkFunction(obj['writeFields']) &&
Utils.checkFunction(obj['readFields']);
}
Utils.checkNull = function(obj) {
return !Utils.isNotNull(obj);
}
/**
* Utils args to array
*
* @param {Object} args arguments
* @return {Array} array
* @api public
*/
Utils.to_array = function(args) {
var len = args.length;
var arr = new Array(len);
for (var i = 0; i < len; i++) {
arr[i] = args[i];
}
return arr;
}
/**
* Utils check is not null
*
* @param {Object} value
* @return {Boolean} true|false
* @api public
*/
Utils.isNotNull = function(value) {
if (value !== null && typeof value !== 'undefined')
return true;
return false;
}
Utils.getType = function(object) {
if (object == null || typeof object === 'undefined') {
return Utils.typeMap['null'];
}
if (Buffer.isBuffer(object)) {
return Utils.typeMap['buffer'];
}
if (Utils.checkArray(object)) {
return Utils.typeMap['array'];
}
if (Utils.checkString(object)) {
return Utils.typeMap['string'];
}
if (Utils.checkObject(object)) {
if (Utils.checkBean(object)) {
return Utils.typeMap['bean'];
}
return Utils.typeMap['object'];
}
if (Utils.checkBoolean(object)) {
return Utils.typeMap['boolean'];
}
if (Utils.checkNumber(object)) {
if (Utils.checkFloat(object)) {
return Utils.typeMap['float'];
}
if (isNaN(object)) {
return Utils.typeMap['null'];
}
return Utils.typeMap['number'];
}
}
var typeArray = ['', 'null', 'buffer', 'array', 'string', 'object', 'bean', 'boolean', 'float', 'number'];
var typeMap = {};
for (var i = 1; i <= typeArray.length; i++) {
typeMap[typeArray[i]] = i;
}
Utils.typeArray = typeArray;
Utils.typeMap = typeMap;
Utils.getBearcat = function() {
return require('bearcat');
}
Utils.genServicesMap = function(services) {
var nMap = {}; // namespace
var sMap = {}; // service
var mMap = {}; // method
var nList = [];
var sList = [];
var mList = [];
var nIndex = 0;
var sIndex = 0;
var mIndex = 0;
for (var namespace in services) {
nList.push(namespace);
nMap[namespace] = nIndex++;
var s = services[namespace];
for (var service in s) {
sList.push(service);
sMap[service] = sIndex++;
var m = s[service];
for (var method in m) {
var func = m[method];
if (Utils.checkFunction(func)) {
mList.push(method);
mMap[method] = mIndex++;
}
}
}
}
return [nMap, sMap, mMap, nList, sList, mList];
}
module.exports = Utils;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| async.js | 20.67% | (142 / 687) | 5.7% | (18 / 316) | 4.41% | (9 / 204) | 20.97% | (142 / 677) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 3 1 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 7 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 | /*!
* async
* https://github.com/caolan/async
*
* Copyright 2010-2014 Caolan McMahon
* Released under the MIT license
*/
(function () {
var async = {};
function noop() {}
function identity(v) {
return v;
}
function toBool(v) {
return !!v;
}
function notId(v) {
return !v;
}
// global on the server, window in the browser
var previous_async;
// Establish the root object, `window` (`self`) in the browser, `global`
// on the server, or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self === 'object' && self.self === self && self ||
typeof global === 'object' && global.global === global && global ||
this;
Eif (root != null) {
previous_async = root.async;
}
async.noConflict = function () {
root.async = previous_async;
return async;
};
function only_once(fn) {
return function() {
if (fn === null) throw new Error("Callback was already called.");
fn.apply(this, arguments);
fn = null;
};
}
function _once(fn) {
return function() {
if (fn === null) return;
fn.apply(this, arguments);
fn = null;
};
}
//// cross-browser compatiblity functions ////
var _toString = Object.prototype.toString;
var _isArray = Array.isArray || function (obj) {
return _toString.call(obj) === '[object Array]';
};
// Ported from underscore.js isObject
var _isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};
function _isArrayLike(arr) {
return _isArray(arr) || (
// has a positive integer length property
typeof arr.length === "number" &&
arr.length >= 0 &&
arr.length % 1 === 0
);
}
function _arrayEach(arr, iterator) {
var index = -1,
length = arr.length;
while (++index < length) {
iterator(arr[index], index, arr);
}
}
function _map(arr, iterator) {
var index = -1,
length = arr.length,
result = Array(length);
while (++index < length) {
result[index] = iterator(arr[index], index, arr);
}
return result;
}
function _range(count) {
return _map(Array(count), function (v, i) { return i; });
}
function _reduce(arr, iterator, memo) {
_arrayEach(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
}
function _forEachOf(object, iterator) {
_arrayEach(_keys(object), function (key) {
iterator(object[key], key);
});
}
function _indexOf(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === item) return i;
}
return -1;
}
var _keys = Object.keys || function (obj) {
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
keys.push(k);
}
}
return keys;
};
function _keyIterator(coll) {
var i = -1;
var len;
var keys;
if (_isArrayLike(coll)) {
len = coll.length;
return function next() {
i++;
return i < len ? i : null;
};
} else {
keys = _keys(coll);
len = keys.length;
return function next() {
i++;
return i < len ? keys[i] : null;
};
}
}
// Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
// This accumulates the arguments passed into an array, after a given index.
// From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
function _restParam(func, startIndex) {
startIndex = startIndex == null ? func.length - 1 : +startIndex;
return function() {
var length = Math.max(arguments.length - startIndex, 0);
var rest = Array(length);
for (var index = 0; index < length; index++) {
rest[index] = arguments[index + startIndex];
}
switch (startIndex) {
case 0: return func.call(this, rest);
case 1: return func.call(this, arguments[0], rest);
}
// Currently unused but handle cases outside of the switch statement:
// var args = Array(startIndex + 1);
// for (index = 0; index < startIndex; index++) {
// args[index] = arguments[index];
// }
// args[startIndex] = rest;
// return func.apply(this, args);
};
}
function _withoutIndex(iterator) {
return function (value, index, callback) {
return iterator(value, callback);
};
}
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
// capture the global reference to guard against fakeTimer mocks
var _setImmediate = typeof setImmediate === 'function' && setImmediate;
var _delay = _setImmediate ? function(fn) {
// not a direct alias for IE10 compatibility
_setImmediate(fn);
} : function(fn) {
setTimeout(fn, 0);
};
Eif (typeof process === 'object' && typeof process.nextTick === 'function') {
async.nextTick = process.nextTick;
} else {
async.nextTick = _delay;
}
async.setImmediate = _setImmediate ? _delay : async.nextTick;
async.forEach =
async.each = function (arr, iterator, callback) {
return async.eachOf(arr, _withoutIndex(iterator), callback);
};
async.forEachSeries =
async.eachSeries = function (arr, iterator, callback) {
return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
};
async.forEachLimit =
async.eachLimit = function (arr, limit, iterator, callback) {
return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
};
async.forEachOf =
async.eachOf = function (object, iterator, callback) {
callback = _once(callback || noop);
object = object || [];
var iter = _keyIterator(object);
var key, completed = 0;
while ((key = iter()) != null) {
completed += 1;
iterator(object[key], key, only_once(done));
}
if (completed === 0) callback(null);
function done(err) {
completed--;
if (err) {
callback(err);
}
// Check key is null in case iterator isn't exhausted
// and done resolved synchronously.
else if (key === null && completed <= 0) {
callback(null);
}
}
};
async.forEachOfSeries =
async.eachOfSeries = function (obj, iterator, callback) {
callback = _once(callback || noop);
obj = obj || [];
var nextKey = _keyIterator(obj);
var key = nextKey();
function iterate() {
var sync = true;
if (key === null) {
return callback(null);
}
iterator(obj[key], key, only_once(function (err) {
if (err) {
callback(err);
}
else {
key = nextKey();
if (key === null) {
return callback(null);
} else {
if (sync) {
async.setImmediate(iterate);
} else {
iterate();
}
}
}
}));
sync = false;
}
iterate();
};
async.forEachOfLimit =
async.eachOfLimit = function (obj, limit, iterator, callback) {
_eachOfLimit(limit)(obj, iterator, callback);
};
function _eachOfLimit(limit) {
return function (obj, iterator, callback) {
callback = _once(callback || noop);
obj = obj || [];
var nextKey = _keyIterator(obj);
if (limit <= 0) {
return callback(null);
}
var done = false;
var running = 0;
var errored = false;
(function replenish () {
if (done && running <= 0) {
return callback(null);
}
while (running < limit && !errored) {
var key = nextKey();
if (key === null) {
done = true;
if (running <= 0) {
callback(null);
}
return;
}
running += 1;
iterator(obj[key], key, only_once(function (err) {
running -= 1;
if (err) {
callback(err);
errored = true;
}
else {
replenish();
}
}));
}
})();
};
}
function doParallel(fn) {
return function (obj, iterator, callback) {
return fn(async.eachOf, obj, iterator, callback);
};
}
function doParallelLimit(fn) {
return function (obj, limit, iterator, callback) {
return fn(_eachOfLimit(limit), obj, iterator, callback);
};
}
function doSeries(fn) {
return function (obj, iterator, callback) {
return fn(async.eachOfSeries, obj, iterator, callback);
};
}
function _asyncMap(eachfn, arr, iterator, callback) {
callback = _once(callback || noop);
arr = arr || [];
var results = _isArrayLike(arr) ? [] : {};
eachfn(arr, function (value, index, callback) {
iterator(value, function (err, v) {
results[index] = v;
callback(err);
});
}, function (err) {
callback(err, results);
});
}
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
async.mapLimit = doParallelLimit(_asyncMap);
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
async.inject =
async.foldl =
async.reduce = function (arr, memo, iterator, callback) {
async.eachOfSeries(arr, function (x, i, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
});
}, function (err) {
callback(err, memo);
});
};
async.foldr =
async.reduceRight = function (arr, memo, iterator, callback) {
var reversed = _map(arr, identity).reverse();
async.reduce(reversed, memo, iterator, callback);
};
async.transform = function (arr, memo, iterator, callback) {
if (arguments.length === 3) {
callback = iterator;
iterator = memo;
memo = _isArray(arr) ? [] : {};
}
async.eachOf(arr, function(v, k, cb) {
iterator(memo, v, k, cb);
}, function(err) {
callback(err, memo);
});
};
function _filter(eachfn, arr, iterator, callback) {
var results = [];
eachfn(arr, function (x, index, callback) {
iterator(x, function (v) {
if (v) {
results.push({index: index, value: x});
}
callback();
});
}, function () {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
}
async.select =
async.filter = doParallel(_filter);
async.selectLimit =
async.filterLimit = doParallelLimit(_filter);
async.selectSeries =
async.filterSeries = doSeries(_filter);
function _reject(eachfn, arr, iterator, callback) {
_filter(eachfn, arr, function(value, cb) {
iterator(value, function(v) {
cb(!v);
});
}, callback);
}
async.reject = doParallel(_reject);
async.rejectLimit = doParallelLimit(_reject);
async.rejectSeries = doSeries(_reject);
function _createTester(eachfn, check, getResult) {
return function(arr, limit, iterator, cb) {
function done() {
if (cb) cb(getResult(false, void 0));
}
function iteratee(x, _, callback) {
if (!cb) return callback();
iterator(x, function (v) {
if (cb && check(v)) {
cb(getResult(true, x));
cb = iterator = false;
}
callback();
});
}
if (arguments.length > 3) {
eachfn(arr, limit, iteratee, done);
} else {
cb = iterator;
iterator = limit;
eachfn(arr, iteratee, done);
}
};
}
async.any =
async.some = _createTester(async.eachOf, toBool, identity);
async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
async.all =
async.every = _createTester(async.eachOf, notId, notId);
async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
function _findGetResult(v, x) {
return x;
}
async.detect = _createTester(async.eachOf, identity, _findGetResult);
async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
iterator(x, function (err, criteria) {
if (err) {
callback(err);
}
else {
callback(null, {value: x, criteria: criteria});
}
});
}, function (err, results) {
if (err) {
return callback(err);
}
else {
callback(null, _map(results.sort(comparator), function (x) {
return x.value;
}));
}
});
function comparator(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}
};
async.auto = function (tasks, concurrency, callback) {
if (typeof arguments[1] === 'function') {
// concurrency is optional, shift the args.
callback = concurrency;
concurrency = null;
}
callback = _once(callback || noop);
var keys = _keys(tasks);
var remainingTasks = keys.length;
if (!remainingTasks) {
return callback(null);
}
if (!concurrency) {
concurrency = remainingTasks;
}
var results = {};
var runningTasks = 0;
var hasError = false;
var listeners = [];
function addListener(fn) {
listeners.unshift(fn);
}
function removeListener(fn) {
var idx = _indexOf(listeners, fn);
if (idx >= 0) listeners.splice(idx, 1);
}
function taskComplete() {
remainingTasks--;
_arrayEach(listeners.slice(0), function (fn) {
fn();
});
}
addListener(function () {
if (!remainingTasks) {
callback(null, results);
}
});
_arrayEach(keys, function (k) {
if (hasError) return;
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
var taskCallback = _restParam(function(err, args) {
runningTasks--;
if (args.length <= 1) {
args = args[0];
}
if (err) {
var safeResults = {};
_forEachOf(results, function(val, rkey) {
safeResults[rkey] = val;
});
safeResults[k] = args;
hasError = true;
callback(err, safeResults);
}
else {
results[k] = args;
async.setImmediate(taskComplete);
}
});
var requires = task.slice(0, task.length - 1);
// prevent dead-locks
var len = requires.length;
var dep;
while (len--) {
if (!(dep = tasks[requires[len]])) {
throw new Error('Has nonexistent dependency in ' + requires.join(', '));
}
if (_isArray(dep) && _indexOf(dep, k) >= 0) {
throw new Error('Has cyclic dependencies');
}
}
function ready() {
return runningTasks < concurrency && _reduce(requires, function (a, x) {
return (a && results.hasOwnProperty(x));
}, true) && !results.hasOwnProperty(k);
}
if (ready()) {
runningTasks++;
task[task.length - 1](taskCallback, results);
}
else {
addListener(listener);
}
function listener() {
if (ready()) {
runningTasks++;
removeListener(listener);
task[task.length - 1](taskCallback, results);
}
}
});
};
async.retry = function(times, task, callback) {
var DEFAULT_TIMES = 5;
var DEFAULT_INTERVAL = 0;
var attempts = [];
var opts = {
times: DEFAULT_TIMES,
interval: DEFAULT_INTERVAL
};
function parseTimes(acc, t){
if(typeof t === 'number'){
acc.times = parseInt(t, 10) || DEFAULT_TIMES;
} else if(typeof t === 'object'){
acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
} else {
throw new Error('Unsupported argument type for \'times\': ' + typeof t);
}
}
var length = arguments.length;
if (length < 1 || length > 3) {
throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
} else if (length <= 2 && typeof times === 'function') {
callback = task;
task = times;
}
if (typeof times !== 'function') {
parseTimes(opts, times);
}
opts.callback = callback;
opts.task = task;
function wrappedTask(wrappedCallback, wrappedResults) {
function retryAttempt(task, finalAttempt) {
return function(seriesCallback) {
task(function(err, result){
seriesCallback(!err || finalAttempt, {err: err, result: result});
}, wrappedResults);
};
}
function retryInterval(interval){
return function(seriesCallback){
setTimeout(function(){
seriesCallback(null);
}, interval);
};
}
while (opts.times) {
var finalAttempt = !(opts.times-=1);
attempts.push(retryAttempt(opts.task, finalAttempt));
if(!finalAttempt && opts.interval > 0){
attempts.push(retryInterval(opts.interval));
}
}
async.series(attempts, function(done, data){
data = data[data.length - 1];
(wrappedCallback || opts.callback)(data.err, data.result);
});
}
// If a callback is passed, run this as a controll flow
return opts.callback ? wrappedTask() : wrappedTask;
};
async.waterfall = function (tasks, callback) {
callback = _once(callback || noop);
if (!_isArray(tasks)) {
var err = new Error('First argument to waterfall must be an array of functions');
return callback(err);
}
if (!tasks.length) {
return callback();
}
function wrapIterator(iterator) {
return _restParam(function (err, args) {
if (err) {
callback.apply(null, [err].concat(args));
}
else {
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
}
else {
args.push(callback);
}
ensureAsync(iterator).apply(null, args);
}
});
}
wrapIterator(async.iterator(tasks))();
};
function _parallel(eachfn, tasks, callback) {
callback = callback || noop;
var results = _isArrayLike(tasks) ? [] : {};
eachfn(tasks, function (task, key, callback) {
task(_restParam(function (err, args) {
if (args.length <= 1) {
args = args[0];
}
results[key] = args;
callback(err);
}));
}, function (err) {
callback(err, results);
});
}
async.parallel = function (tasks, callback) {
_parallel(async.eachOf, tasks, callback);
};
async.parallelLimit = function(tasks, limit, callback) {
_parallel(_eachOfLimit(limit), tasks, callback);
};
async.series = function(tasks, callback) {
_parallel(async.eachOfSeries, tasks, callback);
};
async.iterator = function (tasks) {
function makeCallback(index) {
function fn() {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
}
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
}
return makeCallback(0);
};
async.apply = _restParam(function (fn, args) {
return _restParam(function (callArgs) {
return fn.apply(
null, args.concat(callArgs)
);
});
});
function _concat(eachfn, arr, fn, callback) {
var result = [];
eachfn(arr, function (x, index, cb) {
fn(x, function (err, y) {
result = result.concat(y || []);
cb(err);
});
}, function (err) {
callback(err, result);
});
}
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
callback = callback || noop;
if (test()) {
var next = _restParam(function(err, args) {
if (err) {
callback(err);
} else if (test.apply(this, args)) {
iterator(next);
} else {
callback.apply(null, [null].concat(args));
}
});
iterator(next);
} else {
callback(null);
}
};
async.doWhilst = function (iterator, test, callback) {
var calls = 0;
return async.whilst(function() {
return ++calls <= 1 || test.apply(this, arguments);
}, iterator, callback);
};
async.until = function (test, iterator, callback) {
return async.whilst(function() {
return !test.apply(this, arguments);
}, iterator, callback);
};
async.doUntil = function (iterator, test, callback) {
return async.doWhilst(iterator, function() {
return !test.apply(this, arguments);
}, callback);
};
async.during = function (test, iterator, callback) {
callback = callback || noop;
var next = _restParam(function(err, args) {
if (err) {
callback(err);
} else {
args.push(check);
test.apply(this, args);
}
});
var check = function(err, truth) {
if (err) {
callback(err);
} else if (truth) {
iterator(next);
} else {
callback(null);
}
};
test(check);
};
async.doDuring = function (iterator, test, callback) {
var calls = 0;
async.during(function(next) {
if (calls++ < 1) {
next(null, true);
} else {
test.apply(this, arguments);
}
}, iterator, callback);
};
function _queue(worker, concurrency, payload) {
if (concurrency == null) {
concurrency = 1;
}
else if(concurrency === 0) {
throw new Error('Concurrency must not be zero');
}
function _insert(q, data, pos, callback) {
if (callback != null && typeof callback !== "function") {
throw new Error("task callback must be a function");
}
q.started = true;
if (!_isArray(data)) {
data = [data];
}
if(data.length === 0 && q.idle()) {
// call drain immediately if there are no tasks
return async.setImmediate(function() {
q.drain();
});
}
_arrayEach(data, function(task) {
var item = {
data: task,
callback: callback || noop
};
if (pos) {
q.tasks.unshift(item);
} else {
q.tasks.push(item);
}
if (q.tasks.length === q.concurrency) {
q.saturated();
}
});
async.setImmediate(q.process);
}
function _next(q, tasks) {
return function(){
workers -= 1;
var removed = false;
var args = arguments;
_arrayEach(tasks, function (task) {
_arrayEach(workersList, function (worker, index) {
if (worker === task && !removed) {
workersList.splice(index, 1);
removed = true;
}
});
task.callback.apply(task, args);
});
if (q.tasks.length + workers === 0) {
q.drain();
}
q.process();
};
}
var workers = 0;
var workersList = [];
var q = {
tasks: [],
concurrency: concurrency,
payload: payload,
saturated: noop,
empty: noop,
drain: noop,
started: false,
paused: false,
push: function (data, callback) {
_insert(q, data, false, callback);
},
kill: function () {
q.drain = noop;
q.tasks = [];
},
unshift: function (data, callback) {
_insert(q, data, true, callback);
},
process: function () {
while(!q.paused && workers < q.concurrency && q.tasks.length){
var tasks = q.payload ?
q.tasks.splice(0, q.payload) :
q.tasks.splice(0, q.tasks.length);
var data = _map(tasks, function (task) {
return task.data;
});
if (q.tasks.length === 0) {
q.empty();
}
workers += 1;
workersList.push(tasks[0]);
var cb = only_once(_next(q, tasks));
worker(data, cb);
}
},
length: function () {
return q.tasks.length;
},
running: function () {
return workers;
},
workersList: function () {
return workersList;
},
idle: function() {
return q.tasks.length + workers === 0;
},
pause: function () {
q.paused = true;
},
resume: function () {
if (q.paused === false) { return; }
q.paused = false;
var resumeCount = Math.min(q.concurrency, q.tasks.length);
// Need to call q.process once per concurrent
// worker to preserve full concurrency after pause
for (var w = 1; w <= resumeCount; w++) {
async.setImmediate(q.process);
}
}
};
return q;
}
async.queue = function (worker, concurrency) {
var q = _queue(function (items, cb) {
worker(items[0], cb);
}, concurrency, 1);
return q;
};
async.priorityQueue = function (worker, concurrency) {
function _compareTasks(a, b){
return a.priority - b.priority;
}
function _binarySearch(sequence, item, compare) {
var beg = -1,
end = sequence.length - 1;
while (beg < end) {
var mid = beg + ((end - beg + 1) >>> 1);
if (compare(item, sequence[mid]) >= 0) {
beg = mid;
} else {
end = mid - 1;
}
}
return beg;
}
function _insert(q, data, priority, callback) {
if (callback != null && typeof callback !== "function") {
throw new Error("task callback must be a function");
}
q.started = true;
if (!_isArray(data)) {
data = [data];
}
if(data.length === 0) {
// call drain immediately if there are no tasks
return async.setImmediate(function() {
q.drain();
});
}
_arrayEach(data, function(task) {
var item = {
data: task,
priority: priority,
callback: typeof callback === 'function' ? callback : noop
};
q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
if (q.tasks.length === q.concurrency) {
q.saturated();
}
async.setImmediate(q.process);
});
}
// Start with a normal queue
var q = async.queue(worker, concurrency);
// Override push to accept second parameter representing priority
q.push = function (data, priority, callback) {
_insert(q, data, priority, callback);
};
// Remove unshift function
delete q.unshift;
return q;
};
async.cargo = function (worker, payload) {
return _queue(worker, 1, payload);
};
function _console_fn(name) {
return _restParam(function (fn, args) {
fn.apply(null, args.concat([_restParam(function (err, args) {
if (typeof console === 'object') {
if (err) {
if (console.error) {
console.error(err);
}
}
else if (console[name]) {
_arrayEach(args, function (x) {
console[name](x);
});
}
}
})]));
});
}
async.log = _console_fn('log');
async.dir = _console_fn('dir');
/*async.info = _console_fn('info');
async.warn = _console_fn('warn');
async.error = _console_fn('error');*/
async.memoize = function (fn, hasher) {
var memo = {};
var queues = {};
var has = Object.prototype.hasOwnProperty;
hasher = hasher || identity;
var memoized = _restParam(function memoized(args) {
var callback = args.pop();
var key = hasher.apply(null, args);
if (has.call(memo, key)) {
async.setImmediate(function () {
callback.apply(null, memo[key]);
});
}
else if (has.call(queues, key)) {
queues[key].push(callback);
}
else {
queues[key] = [callback];
fn.apply(null, args.concat([_restParam(function (args) {
memo[key] = args;
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
q[i].apply(null, args);
}
})]));
}
});
memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
async.unmemoize = function (fn) {
return function () {
return (fn.unmemoized || fn).apply(null, arguments);
};
};
function _times(mapper) {
return function (count, iterator, callback) {
mapper(_range(count), iterator, callback);
};
}
async.times = _times(async.map);
async.timesSeries = _times(async.mapSeries);
async.timesLimit = function (count, limit, iterator, callback) {
return async.mapLimit(_range(count), limit, iterator, callback);
};
async.seq = function (/* functions... */) {
var fns = arguments;
return _restParam(function (args) {
var that = this;
var callback = args[args.length - 1];
if (typeof callback == 'function') {
args.pop();
} else {
callback = noop;
}
async.reduce(fns, args, function (newargs, fn, cb) {
fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
cb(err, nextargs);
})]));
},
function (err, results) {
callback.apply(that, [err].concat(results));
});
});
};
async.compose = function (/* functions... */) {
return async.seq.apply(null, Array.prototype.reverse.call(arguments));
};
function _applyEach(eachfn) {
return _restParam(function(fns, args) {
var go = _restParam(function(args) {
var that = this;
var callback = args.pop();
return eachfn(fns, function (fn, _, cb) {
fn.apply(that, args.concat([cb]));
},
callback);
});
if (args.length) {
return go.apply(this, args);
}
else {
return go;
}
});
}
async.applyEach = _applyEach(async.eachOf);
async.applyEachSeries = _applyEach(async.eachOfSeries);
async.forever = function (fn, callback) {
var done = only_once(callback || noop);
var task = ensureAsync(fn);
function next(err) {
if (err) {
return done(err);
}
task(next);
}
next();
};
function ensureAsync(fn) {
return _restParam(function (args) {
var callback = args.pop();
args.push(function () {
var innerArgs = arguments;
if (sync) {
async.setImmediate(function () {
callback.apply(null, innerArgs);
});
} else {
callback.apply(null, innerArgs);
}
});
var sync = true;
fn.apply(this, args);
sync = false;
});
}
async.ensureAsync = ensureAsync;
async.constant = _restParam(function(values) {
var args = [null].concat(values);
return function (callback) {
return callback.apply(this, args);
};
});
async.wrapSync =
async.asyncify = function asyncify(func) {
return _restParam(function (args) {
var callback = args.pop();
var result;
try {
result = func.apply(this, args);
} catch (e) {
return callback(e);
}
// if result is Promise object
if (_isObject(result) && typeof result.then === "function") {
result.then(function(value) {
callback(null, value);
})["catch"](function(err) {
callback(err.message ? err : new Error(err));
});
} else {
callback(null, result);
}
});
};
// Node.js
Eif (typeof module === 'object' && module.exports) {
module.exports = async;
}
// AMD / RequireJS
else if (typeof define === 'function' && define.amd) {
define([], function () {
return async;
});
}
// included directly via <script> tag
else {
root.async = async;
}
}());
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| cronTrigger.js | 11.28% | (22 / 195) | 0% | (0 / 96) | 0% | (0 / 13) | 11.28% | (22 / 195) | |
| job.js | 43.24% | (16 / 37) | 0% | (0 / 8) | 0% | (0 / 5) | 43.24% | (16 / 37) | |
| priorityQueue.js | 25.93% | (14 / 54) | 5.88% | (1 / 17) | 37.5% | (3 / 8) | 25.93% | (14 / 54) | |
| schedule.js | 26.87% | (18 / 67) | 0% | (0 / 26) | 0% | (0 / 7) | 27.27% | (18 / 66) | |
| simpleTrigger.js | 35% | (7 / 20) | 0% | (0 / 15) | 0% | (0 / 4) | 35% | (7 / 20) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* This is the trigger used to decode the cronTimer and calculate the next excution time of the cron Trigger.
*/
var logger = require('log4js').getLogger(__filename);
var SECOND = 0;
var MIN = 1;
var HOUR = 2;
var DOM = 3;
var MONTH = 4;
var DOW = 5;
var Limit = [[0,59],[0,59],[0,24],[1,31],[0,11],[0,6]];
/**
* The constructor of the CronTrigger
* @param trigger The trigger str used to build the cronTrigger instance
*/
var CronTrigger = function(trigger, job){
this.trigger = this.decodeTrigger(trigger);
this.nextTime = this.nextExcuteTime(Date.now());
this.job = job;
};
var pro = CronTrigger.prototype;
/**
* Get the current excuteTime of trigger
*/
pro.excuteTime = function(){
return this.nextTime;
};
/**
* Caculate the next valid cronTime after the given time
* @param The given time point
* @return The nearest valid time after the given time point
*/
pro.nextExcuteTime = function(time){
//add 1s to the time so it must be the next time
time = !!time?time:this.nextTime;
time += 1000;
var cronTrigger = this.trigger;
var date = new Date(time);
date.setMilliseconds(0);
outmost:
while(true){
if(date.getFullYear() > 2999){
logger.error("Can't compute the next time, exceed the limit");
return null;
}
if(!timeMatch(date.getMonth(), cronTrigger[MONTH])){
var nextMonth = nextCronTime(date.getMonth(), cronTrigger[MONTH]);
if(nextMonth == null)
return null;
if(nextMonth <= date.getMonth()){
date.setYear(date.getFullYear() + 1);
date.setMonth(0);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setDate(1);
date.setMonth(nextMonth);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getDate(), cronTrigger[DOM]) || !timeMatch(date.getDay(), cronTrigger[DOW])){
var domLimit = getDomLimit(date.getFullYear(), date.getMonth());
do{
var nextDom = nextCronTime(date.getDate(), cronTrigger[DOM]);
if(nextDom == null)
return null;
//If the date is in the next month, add month
if(nextDom <= date.getDate() || nextDom > domLimit){
date.setDate(1);
date.setMonth(date.getMonth() + 1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
continue outmost;
}
date.setDate(nextDom);
}while(!timeMatch(date.getDay(), cronTrigger[DOW]));
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getHours(), cronTrigger[HOUR])){
var nextHour = nextCronTime(date.getHours(), cronTrigger[HOUR]);
if(nextHour <= date.getHours()){
date.setDate(date.getDate() + 1);
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
continue;
}
date.setHours(nextHour);
date.setMinutes(0);
date.setSeconds(0);
}
if(!timeMatch(date.getMinutes(), cronTrigger[MIN])){
var nextMinute = nextCronTime(date.getMinutes(), cronTrigger[MIN]);
if(nextMinute <= date.getMinutes()){
date.setHours(date.getHours() + 1);
date.setMinutes(nextMinute);
date.setSeconds(0);
continue;
}
date.setMinutes(nextMinute);
date.setSeconds(0);
}
if(!timeMatch(date.getSeconds(), cronTrigger[SECOND])){
var nextSecond = nextCronTime(date.getSeconds(), cronTrigger[SECOND]);
if(nextSecond <= date.getSeconds()){
date.setMinutes(date.getMinutes() + 1);
date.setSeconds(nextSecond);
continue;
}
date.setSeconds(nextSecond);
}
break;
}
this.nextTime = date.getTime();
return this.nextTime;
};
/**
* return the next match time of the given value
* @param value The time value
* @param cronTime The cronTime need to match
* @return The match value or null if unmatch(it offten means an error occur).
*/
function nextCronTime(value, cronTime){
value += 1;
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return value;
else
return cronTime;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value <= cronTime[0] || value > cronTime[cronTime.length -1])
return cronTime[0];
for(var i = 0; i < cronTime.length; i++)
if(value <= cronTime[i])
return cronTime[i];
}
logger.warn('Compute next Time error! value :' + value + ' cronTime : ' + cronTime);
return null;
}
/**
* Match the given value to the cronTime
* @param value The given value
* @param cronTime The cronTime
* @return The match result
*/
function timeMatch(value, cronTime){
if(typeof(cronTime) == 'number'){
if(cronTime == -1)
return true;
if(value == cronTime)
return true;
return false;
}else if(typeof(cronTime) == 'object' && cronTime instanceof Array){
if(value < cronTime[0] || value > cronTime[cronTime.length -1])
return false;
for(var i = 0; i < cronTime.length; i++)
if(value == cronTime[i])
return true;
return false;
}
return null;
}
/**
* Decude the cronTrigger string to arrays
* @param cronTimeStr The cronTimeStr need to decode, like "0 12 * * * 3"
* @return The array to represent the cronTimer
*/
pro.decodeTrigger = function(cronTimeStr){
var cronTimes = cronTimeStr.split(/\s+/);
if(cronTimes.length != 6){
console.log('error');
return null;
}
for(var i = 0; i < cronTimes.length; i++){
cronTimes[i] = (this.decodeTimeStr(cronTimes[i], i));
if(!checkNum(cronTimes[i], Limit[i][0], Limit[i][1])){
logger.error('Decode crontime error, value exceed limit!' +
JSON.stringify({cronTime: cronTimes[i], limit:Limit[i]}));
return null;
}
}
return cronTimes;
}
/**
* Decode the cron Time string
* @param timeStr The cron time string, like: 1,2 or 1-3
* @return A sorted array, like [1,2,3]
*/
pro.decodeTimeStr = function(timeStr, type){
var result = {};
var arr = [];
if(timeStr=='*'){
return -1;
}else if(timeStr.search(',')>0){
var timeArr = timeStr.split(',');
for(var i = 0; i < timeArr.length; i++){
var time = timeArr[i];
if(time.match(/^\d+-\d+$/)){
decodeRangeTime(result, time);
}else if(time.match(/^\d+\/\d+/)){
decodePeriodTime(result, time, type);
}else if(!isNaN(time)){
var num = Number(time);
result[num] = num;
}else
return null;
}
}else if(timeStr.match(/^\d+-\d+$/)){
decodeRangeTime(result, timeStr);
}else if(timeStr.match(/^\d+\/\d+/)){
decodePeriodTime(result, timeStr, type);
}else if(!isNaN(timeStr)){
var num = Number(timeStr);
result[num] = num;
}else{
return null;
}
for(var key in result){
arr.push(result[key]);
}
arr.sort(function(a, b){
return a - b;
});
return arr;
}
/**
* Decode time range
* @param map The decode map
* @param timeStr The range string, like 2-5
*/
function decodeRangeTime(map, timeStr){
var times = timeStr.split('-');
times[0] = Number(times[0]);
times[1] = Number(times[1]);
if(times[0] > times[1]){
console.log("Error time range");
return null;
}
for(var i = times[0]; i <= times[1]; i++){
map[i] = i;
}
}
/**
* Compute the period timer
*/
function decodePeriodTime(map, timeStr, type){
var times = timeStr.split('/');
var min = Limit[type][0];
var max = Limit[type][1];
var remind = Number(times[0]);
var period = Number(times[1]);
if(period==0)
return;
for(var i = min; i <= max; i++){
if(i%period == remind)
map[i] = i;
}
}
/**
* Check if the numbers are valid
* @param nums The numbers array need to check
* @param min Minimus value
* @param max Maximam value
* @return If all the numbers are in the data range
*/
function checkNum(nums, min, max){
if(nums == null)
return false;
if(nums == -1)
return true;
for(var i = 0; i < nums.length; i++){
if(nums[i]<min || nums[i]>max)
return false;
}
return true;
}
/**
* Get the date limit of given month
* @param The given year
* @month The given month
* @return The date count of given month
*/
function getDomLimit(year, month){
var date = new Date(year, month+1, 0);
return date.getDate();
}
/**
* Create cronTrigger
* @param trigger The Cron Trigger string
* @return The Cron trigger
*/
function createTrigger(trigger, job){
return new CronTrigger(trigger, job);
}
module.exports.createTrigger = createTrigger;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* This is the class of the job used in schedule module
*/
var cronTrigger = require('./cronTrigger');
var simpleTrigger = require('./simpleTrigger');
var jobId = 1;
var SIMPLE_JOB = 1;
var CRON_JOB = 2;
var jobCount = 0;
var warnLimit = 500;
var logger = require('log4js').getLogger(__filename);
//For test
var lateCount = 0;
var Job = function(trigger, jobFunc, jobData){
this.data = (!!jobData)?jobData:null;
this.func = jobFunc;
if(typeof(trigger) == 'string'){
this.type = CRON_JOB;
this.trigger = cronTrigger.createTrigger(trigger, this);
}else if(typeof(trigger) == 'object'){
this.type = SIMPLE_JOB;
this.trigger = simpleTrigger.createTrigger(trigger, this);
}
this.id = jobId++;
this.runTime = 0;
};
var pro = Job.prototype;
/**
* Run the job code
*/
pro.run = function(){
try{
jobCount++;
this.runTime++;
var late = Date.now() - this.excuteTime();
if(late>warnLimit)
logger.warn('run Job count ' + jobCount + ' late :' + late + ' lateCount ' + (++lateCount));
this.func(this.data);
}catch(e){
logger.error("Job run error for exception ! " + e.stack);
}
};
/**
* Compute the next excution time
*/
pro.nextTime = function(){
return this.trigger.nextExcuteTime();
};
pro.excuteTime = function(){
return this.trigger.excuteTime();
};
/**
* The Interface to create Job
* @param trigger The trigger to use
* @param jobFunc The function the job to run
* @param jobDate The date the job use
* @return The new instance of the give job or null if fail
*/
function createJob(trigger, jobFunc, jobData){
return new Job(trigger, jobFunc, jobData);
}
module.exports.createJob = createJob;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* The PriorityQeueu class
*/
var PriorityQueue = function(comparator){
this.init(comparator);
}
var pro = PriorityQueue.prototype;
pro.init = function(comparator){
this._comparator = typeof(comparator)=='function'?comparator:this._defaultComparator;
this._queue = [];
this._tailPos = 0;
}
/**
* Return the size of the pirority queue
* @return PirorityQueue size
*/
pro.size = function(){
return this._tailPos;
};
/**
* Insert an element to the queue
* @param element The element to insert
*/
pro.offer = function(element){
var queue = this._queue;
var compare = this._comparator;
queue[this._tailPos++] = element;
var pos = this._tailPos-1;
while(pos > 0){
var parentPos = (pos%2==0)?(pos/2-1):(pos-1)/2;
if(compare(queue[parentPos], element)){
queue[pos] = queue[parentPos];
queue[parentPos] = element;
pos = parentPos;
}else{
break;
}
}
};
/**
* Get and remove the first element in the queue
* @return The first element
*/
pro.pop = function(){
var queue = this._queue;
var compare = this._comparator;
if(this._tailPos == 0)
return null;
var headNode = queue[0];
var tail = queue[this._tailPos - 1];
var pos = 0;
var left = pos*2 + 1;
var right = left + 1;
queue[pos] = tail;
this._tailPos--;
while(left < this._tailPos){
if(right<this._tailPos && compare(queue[left], queue[right]) && compare(queue[pos], queue[right])){
queue[pos] = queue[right];
queue[right] = tail;
pos = right;
}else if(compare(queue[pos],queue[left])){
queue[pos] = queue[left];
queue[left] = tail;
pos = left;
}else{
break;
}
left = pos*2 + 1;
right = left + 1;
}
return headNode;
};
/**
* Get but not remove the first element in the queue
* @return The first element
*/
pro.peek = function(){
if(this._tailPos == 0)
return null;
return this._queue[0];
}
pro._defaultComparator = function(a , b){
return a > b;
}
module.exports.createPriorityQueue = function(comparator){
return new PriorityQueue(comparator);
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* The main class and interface of the schedule module
*/
var PriorityQueue = require('./priorityQueue');
var Job = require('./job.js');
var timerCount = 0;
var logger = require('log4js').getLogger(__filename);
var map = {};
var queue = PriorityQueue.createPriorityQueue(comparator);
var jobId = 0;
var timer;
//The accuracy of the scheduler, it will affect the performance when the schedule tasks are
//crowded together
var accuracy = 10;
/**
* Schedule a new Job
*/
function scheduleJob(trigger, jobFunc, jobData){
var job = Job.createJob(trigger, jobFunc, jobData);
var excuteTime = job.excuteTime();
var id = job.id;
map[id] = job;
var element = {
id : id,
time : excuteTime
};
var curJob = queue.peek();
if(!curJob || excuteTime < curJob.time){
queue.offer(element);
setTimer(job);
return job.id;
}
queue.offer(element);
return job.id;
}
/**
* Cancel Job
*/
function cancelJob(id){
var curJob = queue.peek();
if(curJob && id === curJob.id){ // to avoid queue.peek() is null
queue.pop();
delete map[id];
clearTimeout(timer);
excuteJob();
}
delete map[id];
return true;
}
/**
* Clear last timeout and schedule the next job, it will automaticly run the job that
* need to run now
* @param job The job need to schedule
* @return void
*/
function setTimer(job){
clearTimeout(timer);
timer = setTimeout(excuteJob, job.excuteTime()-Date.now());
}
/**
* The function used to ran the schedule job, and setTimeout for next running job
*/
function excuteJob(){
var job = peekNextJob();
var nextJob;
while(!!job && (job.excuteTime()-Date.now())<accuracy){
job.run();
queue.pop();
var nextTime = job.nextTime();
if(nextTime === null){
delete map[job.id];
}else{
queue.offer({id:job.id, time: nextTime});
}
job = peekNextJob();
}
//If all the job have been canceled
if(!job)
return;
//Run next schedule
setTimer(job);
}
/**
* Return, but not remove the next valid job
* @return Next valid job
*/
function peekNextJob(){
if(queue.size() <= 0)
return null;
var job = null;
do{
job = map[queue.peek().id];
if(!job) queue.pop();
}while(!job && queue.size() > 0);
return (!!job)?job:null;
}
/**
* Return and remove the next valid job
* @return Next valid job
*/
function getNextJob(){
var job = null;
while(!job && queue.size() > 0){
var id = queue.pop().id;
job = map[id];
}
return (!!job)?job:null;
}
function comparator(e1, e2){
return e1.time > e2.time;
}
module.exports.scheduleJob = scheduleJob;
module.exports.cancelJob = cancelJob;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 1 1 1 1 1 1 1 | /** * This is the tirgger that use an object as trigger. */ var SKIP_OLD_JOB = false; /** * The constructor of simple trigger */ var SimpleTrigger = function(trigger, job){ this.nextTime = (!!trigger.start)?trigger.start:Date.now(); //The rec this.period = (!!trigger.period)?trigger.period:-1; //The running count of the job, -1 means no limit this.count = (!!trigger.count)?trigger.count:-1; this.job = job; }; var pro = SimpleTrigger.prototype; /** * Get the current excuteTime of rigger */ pro.excuteTime = function(){ return this.nextTime; }; /** * Get the next excuteTime of the trigger, and set the trigger's excuteTime * @return Next excute time */ pro.nextExcuteTime = function(){ var period = this.period; if((this.count > 0 && this.count <= this.job.runTime) || period <= 0) return null; this.nextTime += period; if(SKIP_OLD_JOB && this.nextTime < Date.now()){ this.nextTime += Math.floor((Date.now()-this.nextTime)/period) * period; } return this.nextTime; }; /** * Create Simple trigger */ function createTrigger(trigger, job){ return new SimpleTrigger(trigger, job); } module.exports.createTrigger = createTrigger; |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| connect-logger.js | 10.34% | (6 / 58) | 0% | (0 / 66) | 0% | (0 / 10) | 11.54% | (6 / 52) | |
| date_format.js | 21.62% | (8 / 37) | 0% | (0 / 10) | 0% | (0 / 4) | 21.62% | (8 / 37) | |
| layouts.js | 22.61% | (26 / 115) | 2.17% | (1 / 46) | 0% | (0 / 32) | 22.81% | (26 / 114) | |
| levels.js | 51.85% | (14 / 27) | 25% | (3 / 12) | 33.33% | (2 / 6) | 51.85% | (14 / 27) | |
| log4js.js | 63.78% | (81 / 127) | 38.1% | (24 / 63) | 61.54% | (16 / 26) | 64.29% | (81 / 126) | |
| logger.js | 52.94% | (18 / 34) | 25% | (2 / 8) | 22.22% | (2 / 9) | 52.94% | (18 / 34) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | 1 1 1 1 1 1 | "use strict";
var levels = require("./levels");
var DEFAULT_FORMAT = ':remote-addr - -' +
' ":method :url HTTP/:http-version"' +
' :status :content-length ":referrer"' +
' ":user-agent"';
/**
* Log requests with the given `options` or a `format` string.
*
* Options:
*
* - `format` Format string, see below for tokens
* - `level` A log4js levels instance. Supports also 'auto'
*
* Tokens:
*
* - `:req[header]` ex: `:req[Accept]`
* - `:res[header]` ex: `:res[Content-Length]`
* - `:http-version`
* - `:response-time`
* - `:remote-addr`
* - `:date`
* - `:method`
* - `:url`
* - `:referrer`
* - `:user-agent`
* - `:status`
*
* @param {String|Function|Object} format or options
* @return {Function}
* @api public
*/
function getLogger(logger4js, options) {
if ('object' == typeof options) {
options = options || {};
} else if (options) {
options = { format: options };
} else {
options = {};
}
var thislogger = logger4js
, level = levels.toLevel(options.level, levels.INFO)
, fmt = options.format || DEFAULT_FORMAT
, nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
return function (req, res, next) {
// mount safety
if (req._logging) return next();
// nologs
if (nolog && nolog.test(req.originalUrl)) return next();
if (thislogger.isLevelEnabled(level) || options.level === 'auto') {
var start = new Date()
, statusCode
, writeHead = res.writeHead
, end = res.end
, url = req.originalUrl;
// flag as logging
req._logging = true;
// proxy for statusCode.
res.writeHead = function(code, headers){
res.writeHead = writeHead;
res.writeHead(code, headers);
res.__statusCode = statusCode = code;
res.__headers = headers || {};
//status code response level handling
if(options.level === 'auto'){
level = levels.INFO;
if(code >= 300) level = levels.WARN;
if(code >= 400) level = levels.ERROR;
} else {
level = levels.toLevel(options.level, levels.INFO);
}
};
// proxy end to output a line to the provided logger.
res.end = function(chunk, encoding) {
res.end = end;
res.end(chunk, encoding);
res.responseTime = new Date() - start;
if (thislogger.isLevelEnabled(level)) {
if (typeof fmt === 'function') {
var line = fmt(req, res, function(str){ return format(str, req, res); });
if (line) thislogger.log(level, line);
} else {
thislogger.log(level, format(fmt, req, res));
}
}
};
}
//ensure next gets always called
next();
};
}
/**
* Return formatted log line.
*
* @param {String} str
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @return {String}
* @api private
*/
function format(str, req, res) {
return str
.replace(':url', req.originalUrl)
.replace(':method', req.method)
.replace(':status', res.__statusCode || res.statusCode)
.replace(':response-time', res.responseTime)
.replace(':date', new Date().toUTCString())
.replace(':referrer', req.headers.referer || req.headers.referrer || '')
.replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor)
.replace(
':remote-addr',
req.socket &&
(req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress))
)
.replace(':user-agent', req.headers['user-agent'] || '')
.replace(
':content-length',
(res._headers && res._headers['content-length']) ||
(res.__headers && res.__headers['Content-Length']) ||
'-'
)
.replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; })
.replace(/:res\[([^\]]+)\]/g, function(_, field){
return res._headers ?
(res._headers[field.toLowerCase()] || res.__headers[field])
: (res.__headers && res.__headers[field]);
});
}
/**
* Return RegExp Object about nolog
*
* @param {String} nolog
* @return {RegExp}
* @api private
*
* syntax
* 1. String
* 1.1 "\\.gif"
* NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga
* LOGGING http://example.com/hoge.agif
* 1.2 in "\\.gif|\\.jpg$"
* NOT LOGGING http://example.com/hoge.gif and
* http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga
* LOGGING http://example.com/hoge.agif,
* http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge
* 1.3 in "\\.(gif|jpe?g|png)$"
* NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg
* LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3
* 2. RegExp
* 2.1 in /\.(gif|jpe?g|png)$/
* SAME AS 1.3
* 3. Array
* 3.1 ["\\.jpg$", "\\.png", "\\.gif"]
* SAME AS "\\.jpg|\\.png|\\.gif"
*/
function createNoLogCondition(nolog) {
var regexp = null;
if (nolog) {
if (nolog instanceof RegExp) {
regexp = nolog;
}
if (typeof nolog === 'string') {
regexp = new RegExp(nolog);
}
if (Array.isArray(nolog)) {
var regexpsAsStrings = nolog.map(
function convertToStrings(o) {
return o.source ? o.source : o;
}
);
regexp = new RegExp(regexpsAsStrings.join('|'));
}
}
return regexp;
}
exports.connectLogger = getLogger;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 1 1 1 1 1 1 1 1 | "use strict"; exports.ISO8601_FORMAT = "yyyy-MM-dd hh:mm:ss.SSS"; exports.ISO8601_WITH_TZ_OFFSET_FORMAT = "yyyy-MM-ddThh:mm:ssO"; exports.DATETIME_FORMAT = "dd MM yyyy hh:mm:ss.SSS"; exports.ABSOLUTETIME_FORMAT = "hh:mm:ss.SSS"; function padWithZeros(vNumber, width) { var numAsString = vNumber + ""; while (numAsString.length < width) { numAsString = "0" + numAsString; } return numAsString; } function addZero(vNumber) { return padWithZeros(vNumber, 2); } /** * Formats the TimeOffest * Thanks to http://www.svendtofte.com/code/date_format/ * @private */ function offset(date) { // Difference to Greenwich time (GMT) in hours var os = Math.abs(date.getTimezoneOffset()); var h = String(Math.floor(os/60)); var m = String(os%60); if (h.length == 1) { h = "0" + h; } if (m.length == 1) { m = "0" + m; } return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; } exports.asString = function(/*format,*/ date) { var format = exports.ISO8601_FORMAT; if (typeof(date) === "string") { format = arguments[0]; date = arguments[1]; } var vDay = addZero(date.getDate()); var vMonth = addZero(date.getMonth()+1); var vYearLong = addZero(date.getFullYear()); var vYearShort = addZero(date.getFullYear().toString().substring(3,4)); var vYear = (format.indexOf("yyyy") > -1 ? vYearLong : vYearShort); var vHour = addZero(date.getHours()); var vMinute = addZero(date.getMinutes()); var vSecond = addZero(date.getSeconds()); var vMillisecond = padWithZeros(date.getMilliseconds(), 3); var vTimeZone = offset(date); var formatted = format .replace(/dd/g, vDay) .replace(/MM/g, vMonth) .replace(/y{1,4}/g, vYear) .replace(/hh/g, vHour) .replace(/mm/g, vMinute) .replace(/ss/g, vSecond) .replace(/SSS/g, vMillisecond) .replace(/O/g, vTimeZone); return formatted; }; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | "use strict";
var dateFormat = require('./date_format')
, os = require('os')
, eol = os.EOL || '\n'
, util = require('util')
, replacementRegExp = /%[sdj]/g
, layoutMakers = {
"messagePassThrough": function() { return messagePassThroughLayout; },
"basic": function() { return basicLayout; },
"colored": function() { return colouredLayout; },
"coloured": function() { return colouredLayout; },
"pattern": function (config) {
return patternLayout(config && config.pattern, config && config.tokens);
}
}
, colours = {
ALL: "grey",
TRACE: "blue",
DEBUG: "cyan",
INFO: "green",
WARN: "yellow",
ERROR: "red",
FATAL: "magenta",
OFF: "grey"
};
function wrapErrorsWithInspect(items) {
return items.map(function(item) {
if ((item instanceof Error) && item.stack) {
return { inspect: function() { return util.format(item) + '\n' + item.stack; } };
} else {
return item;
}
});
}
function formatLogData(logData) {
var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments);
return util.format.apply(util, wrapErrorsWithInspect(data));
}
var styles = {
//styles
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
//grayscale
'white' : [37, 39],
'grey' : [90, 39],
'black' : [90, 39],
//colors
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
function colorizeStart(style) {
return style ? '\x1B[' + styles[style][0] + 'm' : '';
}
function colorizeEnd(style) {
return style ? '\x1B[' + styles[style][1] + 'm' : '';
}
/**
* Taken from masylum's fork (https://github.com/masylum/log4js-node)
*/
function colorize (str, style) {
return colorizeStart(style) + str + colorizeEnd(style);
}
function timestampLevelAndCategory(loggingEvent, colour) {
var output = colorize(
formatLogData(
'[%s] [%s] %s - '
, dateFormat.asString(loggingEvent.startTime)
, loggingEvent.level
, loggingEvent.categoryName
)
, colour
);
return output;
}
/**
* BasicLayout is a simple layout for storing the logs. The logs are stored
* in following format:
* <pre>
* [startTime] [logLevel] categoryName - message\n
* </pre>
*
* @author Stephan Strittmatter
*/
function basicLayout (loggingEvent) {
return timestampLevelAndCategory(loggingEvent) + formatLogData(loggingEvent.data);
}
/**
* colouredLayout - taken from masylum's fork.
* same as basicLayout, but with colours.
*/
function colouredLayout (loggingEvent) {
return timestampLevelAndCategory(
loggingEvent,
colours[loggingEvent.level.toString()]
) + formatLogData(loggingEvent.data);
}
function messagePassThroughLayout (loggingEvent) {
return formatLogData(loggingEvent.data);
}
/**
* PatternLayout
* Format for specifiers is %[padding].[truncation][field]{[format]}
* e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
* Fields can be any of:
* - %r time in toLocaleTimeString format
* - %p log level
* - %c log category
* - %m log data
* - %d date in various formats
* - %% %
* - %n newline
* - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
* You can use %[ and %] to define a colored block.
*
* Tokens are specified as simple key:value objects.
* The key represents the token name whereas the value can be a string or function
* which is called to extract the value to put in the log message. If token is not
* found, it doesn't replace the field.
*
* A sample token would be: { "pid" : function() { return process.pid; } }
*
* Takes a pattern string, array of tokens and returns a layout function.
* @param {String} Log format pattern String
* @param {object} map object of different tokens
* @return {Function}
* @author Stephan Strittmatter
* @author Jan Schmidle
*/
function patternLayout (pattern, tokens) {
var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdmnprx%])(\{([^\}]+)\})?|([^%]+)/;
pattern = pattern || TTCC_CONVERSION_PATTERN;
function categoryName(loggingEvent, specifier) {
var loggerName = loggingEvent.categoryName;
if (specifier) {
var precision = parseInt(specifier, 10);
var loggerNameBits = loggerName.split(".");
if (precision < loggerNameBits.length) {
loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
}
}
return loggerName;
}
function formatAsDate(loggingEvent, specifier) {
var format = dateFormat.ISO8601_FORMAT;
if (specifier) {
format = specifier;
// Pick up special cases
if (format == "ISO8601") {
format = dateFormat.ISO8601_FORMAT;
} else if (format == "ABSOLUTE") {
format = dateFormat.ABSOLUTETIME_FORMAT;
} else if (format == "DATE") {
format = dateFormat.DATETIME_FORMAT;
}
}
// Format the date
return dateFormat.asString(format, loggingEvent.startTime);
}
function formatMessage(loggingEvent) {
return formatLogData(loggingEvent.data);
}
function endOfLine() {
return eol;
}
function logLevel(loggingEvent) {
return loggingEvent.level.toString();
}
function startTime(loggingEvent) {
return "" + loggingEvent.startTime.toLocaleTimeString();
}
function startColour(loggingEvent) {
return colorizeStart(colours[loggingEvent.level.toString()]);
}
function endColour(loggingEvent) {
return colorizeEnd(colours[loggingEvent.level.toString()]);
}
function percent() {
return '%';
}
function userDefined(loggingEvent, specifier) {
if (typeof(tokens[specifier]) !== 'undefined') {
if (typeof(tokens[specifier]) === 'function') {
return tokens[specifier](loggingEvent);
} else {
return tokens[specifier];
}
}
return null;
}
var replacers = {
'c': categoryName,
'd': formatAsDate,
'm': formatMessage,
'n': endOfLine,
'p': logLevel,
'r': startTime,
'[': startColour,
']': endColour,
'%': percent,
'x': userDefined
};
function replaceToken(conversionCharacter, loggingEvent, specifier) {
return replacers[conversionCharacter](loggingEvent, specifier);
}
function truncate(truncation, toTruncate) {
var len;
if (truncation) {
len = parseInt(truncation.substr(1), 10);
return toTruncate.substring(0, len);
}
return toTruncate;
}
function pad(padding, toPad) {
var len;
if (padding) {
if (padding.charAt(0) == "-") {
len = parseInt(padding.substr(1), 10);
// Right pad with spaces
while (toPad.length < len) {
toPad += " ";
}
} else {
len = parseInt(padding, 10);
// Left pad with spaces
while (toPad.length < len) {
toPad = " " + toPad;
}
}
}
return toPad;
}
return function(loggingEvent) {
var formattedString = "";
var result;
var searchString = pattern;
while ((result = regex.exec(searchString))) {
var matchedString = result[0];
var padding = result[1];
var truncation = result[2];
var conversionCharacter = result[3];
var specifier = result[5];
var text = result[6];
// Check if the pattern matched was just normal text
if (text) {
formattedString += "" + text;
} else {
// Create a raw replacement string based on the conversion
// character and specifier
var replacement =
replaceToken(conversionCharacter, loggingEvent, specifier) ||
matchedString;
// Format the replacement according to any padding or
// truncation specified
replacement = truncate(truncation, replacement);
replacement = pad(padding, replacement);
formattedString += replacement;
}
searchString = searchString.substr(result.index + result[0].length);
}
return formattedString;
};
}
module.exports = {
basicLayout: basicLayout,
messagePassThroughLayout: messagePassThroughLayout,
patternLayout: patternLayout,
colouredLayout: colouredLayout,
coloredLayout: colouredLayout,
layout: function(name, config) {
return layoutMakers[name] && layoutMakers[name](config);
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 1 8 8 1 6 6 6 6 6 1 1 1 1 1 | "use strict";
function Level(level, levelStr) {
this.level = level;
this.levelStr = levelStr;
}
/**
* converts given String to corresponding Level
* @param {String} sArg String value of Level OR Log4js.Level
* @param {Log4js.Level} defaultLevel default Level, if no String representation
* @return Level object
* @type Log4js.Level
*/
function toLevel(sArg, defaultLevel) {
Iif (!sArg) {
return defaultLevel;
}
Eif (typeof sArg == "string") {
var s = sArg.toUpperCase();
Eif (module.exports[s]) {
return module.exports[s];
} else {
return defaultLevel;
}
}
return toLevel(sArg.toString());
}
Level.prototype.toString = function() {
return this.levelStr;
};
Level.prototype.isLessThanOrEqualTo = function(otherLevel) {
if (typeof otherLevel === "string") {
otherLevel = toLevel(otherLevel);
}
return this.level <= otherLevel.level;
};
Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) {
if (typeof otherLevel === "string") {
otherLevel = toLevel(otherLevel);
}
return this.level >= otherLevel.level;
};
Level.prototype.isEqualTo = function(otherLevel) {
if (typeof otherLevel == "string") {
otherLevel = toLevel(otherLevel);
}
return this.level === otherLevel.level;
};
module.exports = {
ALL: new Level(Number.MIN_VALUE, "ALL"),
TRACE: new Level(5000, "TRACE"),
DEBUG: new Level(10000, "DEBUG"),
INFO: new Level(20000, "INFO"),
WARN: new Level(30000, "WARN"),
ERROR: new Level(40000, "ERROR"),
FATAL: new Level(50000, "FATAL"),
OFF: new Level(Number.MAX_VALUE, "OFF"),
toLevel: toLevel
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | 1 1 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 | "use strict";
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview log4js is a library to log in JavaScript in similar manner
* than in log4j for Java. The API should be nearly the same.
*
* <h3>Example:</h3>
* <pre>
* var logging = require('log4js');
* //add an appender that logs all messages to stdout.
* logging.addAppender(logging.consoleAppender());
* //add an appender that logs "some-category" to a file
* logging.addAppender(logging.fileAppender("file.log"), "some-category");
* //get a logger
* var log = logging.getLogger("some-category");
* log.setLevel(logging.levels.TRACE); //set the Level
*
* ...
*
* //call the log
* log.trace("trace me" );
* </pre>
*
* NOTE: the authors below are the original browser-based log4js authors
* don't try to contact them about bugs in this version :)
* @version 1.0
* @author Stephan Strittmatter - http://jroller.com/page/stritti
* @author Seth Chisamore - http://www.chisamore.com
* @since 2005-05-20
* @static
* Website: http://log4js.berlios.de
*/
var events = require('events')
, fs = require('fs')
, path = require('path')
, util = require('util')
, layouts = require('./layouts')
, levels = require('./levels')
, LoggingEvent = require('./logger').LoggingEvent
, Logger = require('./logger').Logger
, ALL_CATEGORIES = '[all]'
, appenders = {}
, loggers = {}
, appenderMakers = {}
, defaultConfig = {
appenders: [
{ type: "console" }
],
replaceConsole: false
};
/**
* Get a logger instance. Instance is cached on categoryName level.
* @param {String} categoryName name of category to log to.
* @return {Logger} instance of logger for the category
* @static
*/
function getLogger (categoryName) {
// Use default logger if categoryName is not specified or invalid
Iif (typeof categoryName !== "string") {
categoryName = Logger.DEFAULT_CATEGORY;
}
var appenderList;
Eif (!loggers[categoryName]) {
// Create the logger for this name if it doesn't already exist
loggers[categoryName] = new Logger(categoryName);
Iif (appenders[categoryName]) {
appenderList = appenders[categoryName];
appenderList.forEach(function(appender) {
loggers[categoryName].addListener("log", appender);
});
}
Eif (appenders[ALL_CATEGORIES]) {
appenderList = appenders[ALL_CATEGORIES];
appenderList.forEach(function(appender) {
loggers[categoryName].addListener("log", appender);
});
}
}
return loggers[categoryName];
}
/**
* args are appender, then zero or more categories
*/
function addAppender () {
var args = Array.prototype.slice.call(arguments);
var appender = args.shift();
Eif (args.length === 0 || args[0] === undefined) {
args = [ ALL_CATEGORIES ];
}
//argument may already be an array
Iif (Array.isArray(args[0])) {
args = args[0];
}
args.forEach(function(category) {
addAppenderToCategory(appender, category);
Eif (category === ALL_CATEGORIES) {
addAppenderToAllLoggers(appender);
} else if (loggers[category]) {
loggers[category].addListener("log", appender);
}
});
}
function addAppenderToAllLoggers(appender) {
for (var logger in loggers) {
if (loggers.hasOwnProperty(logger)) {
loggers[logger].addListener("log", appender);
}
}
}
function addAppenderToCategory(appender, category) {
Eif (!appenders[category]) {
appenders[category] = [];
}
appenders[category].push(appender);
}
function clearAppenders () {
appenders = {};
for (var logger in loggers) {
if (loggers.hasOwnProperty(logger)) {
loggers[logger].removeAllListeners("log");
}
}
}
function configureAppenders(appenderList, options) {
clearAppenders();
Eif (appenderList) {
appenderList.forEach(function(appenderConfig) {
loadAppender(appenderConfig.type);
var appender;
appenderConfig.makers = appenderMakers;
try {
appender = appenderMakers[appenderConfig.type](appenderConfig, options);
addAppender(appender, appenderConfig.category);
} catch(e) {
throw new Error("log4js configuration problem for " + util.inspect(appenderConfig), e);
}
});
}
}
function configureLevels(levels) {
Iif (levels) {
for (var category in levels) {
if (levels.hasOwnProperty(category)) {
getLogger(category).setLevel(levels[category]);
}
}
}
}
function setGlobalLogLevel(level) {
Logger.prototype.level = levels.toLevel(level, levels.TRACE);
}
/**
* Get the default logger instance.
* @return {Logger} instance of default logger
* @static
*/
function getDefaultLogger () {
return getLogger(Logger.DEFAULT_CATEGORY);
}
var configState = {};
function loadConfigurationFile(filename) {
Iif (filename) {
return JSON.parse(fs.readFileSync(filename, "utf8"));
}
return undefined;
}
function configureOnceOff(config, options) {
Eif (config) {
try {
configureAppenders(config.appenders, options);
configureLevels(config.levels);
Iif (config.replaceConsole) {
replaceConsole();
} else {
restoreConsole();
}
} catch (e) {
throw new Error(
"Problem reading log4js config " + util.inspect(config) +
". Error was \"" + e.message + "\" (" + e.stack + ")"
);
}
}
}
function reloadConfiguration() {
var mtime = getMTime(configState.filename);
if (!mtime) return;
if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) {
configureOnceOff(loadConfigurationFile(configState.filename));
}
configState.lastMTime = mtime;
}
function getMTime(filename) {
var mtime;
try {
mtime = fs.statSync(configState.filename).mtime;
} catch (e) {
getLogger('log4js').warn('Failed to load configuration file ' + filename);
}
return mtime;
}
function initReloadConfiguration(filename, options) {
if (configState.timerId) {
clearInterval(configState.timerId);
delete configState.timerId;
}
configState.filename = filename;
configState.lastMTime = getMTime(filename);
configState.timerId = setInterval(reloadConfiguration, options.reloadSecs*1000);
}
function configure(configurationFileOrObject, options) {
var config = configurationFileOrObject;
config = config || process.env.LOG4JS_CONFIG;
options = options || {};
Eif (config === undefined || config === null || typeof(config) === 'string') {
Iif (options.reloadSecs) {
initReloadConfiguration(config, options);
}
config = loadConfigurationFile(config) || defaultConfig;
} else {
if (options.reloadSecs) {
getLogger('log4js').warn(
'Ignoring configuration reload parameter for "object" configuration.'
);
}
}
configureOnceOff(config, options);
}
var originalConsoleFunctions = {
log: console.log,
debug: console.debug,
info: console.info,
warn: console.warn,
error: console.error
};
function replaceConsole(logger) {
function replaceWith(fn) {
return function() {
fn.apply(logger, arguments);
};
}
logger = logger || getLogger("console");
['log','debug','info','warn','error'].forEach(function (item) {
console[item] = replaceWith(item === 'log' ? logger.info : logger[item]);
});
}
function restoreConsole() {
['log', 'debug', 'info', 'warn', 'error'].forEach(function (item) {
console[item] = originalConsoleFunctions[item];
});
}
function loadAppender(appender) {
var appenderModule;
try {
appenderModule = require('./appenders/' + appender);
} catch (e) {
appenderModule = require(appender);
}
module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule);
appenderMakers[appender] = appenderModule.configure.bind(appenderModule);
}
module.exports = {
getLogger: getLogger,
getDefaultLogger: getDefaultLogger,
addAppender: addAppender,
loadAppender: loadAppender,
clearAppenders: clearAppenders,
configure: configure,
replaceConsole: replaceConsole,
restoreConsole: restoreConsole,
levels: levels,
setGlobalLogLevel: setGlobalLogLevel,
layouts: layouts,
appenders: {},
appenderMakers: appenderMakers,
connectLogger: require('./connect-logger').connectLogger
};
//set ourselves up
configure();
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | 1 1 1 3 3 1 1 1 1 1 1 1 1 6 6 6 1 1 | "use strict";
var levels = require('./levels')
, util = require('util')
, events = require('events')
, DEFAULT_CATEGORY = '[default]';
/**
* Models a logging event.
* @constructor
* @param {String} categoryName name of category
* @param {Log4js.Level} level level of message
* @param {Array} data objects to log
* @param {Log4js.Logger} logger the associated logger
* @author Seth Chisamore
*/
function LoggingEvent (categoryName, level, data, logger) {
this.startTime = new Date();
this.categoryName = categoryName;
this.data = data;
this.level = level;
this.logger = logger;
}
/**
* Logger to log messages.
* use {@see Log4js#getLogger(String)} to get an instance.
* @constructor
* @param name name of category to log to
* @author Stephan Strittmatter
*/
function Logger (name, level) {
this.category = name || DEFAULT_CATEGORY;
Iif (level) {
this.setLevel(level);
}
}
util.inherits(Logger, events.EventEmitter);
Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY;
Logger.prototype.level = levels.TRACE;
Logger.prototype.setLevel = function(level) {
this.level = levels.toLevel(level, this.level || levels.TRACE);
};
Logger.prototype.removeLevel = function() {
delete this.level;
};
Logger.prototype.log = function() {
var args = Array.prototype.slice.call(arguments)
, logLevel = args.shift()
, loggingEvent = new LoggingEvent(this.category, logLevel, args, this);
this.emit("log", loggingEvent);
};
Logger.prototype.isLevelEnabled = function(otherLevel) {
return this.level.isLessThanOrEqualTo(otherLevel);
};
['Trace','Debug','Info','Warn','Error','Fatal'].forEach(
function(levelString) {
var level = levels.toLevel(levelString);
Logger.prototype['is'+levelString+'Enabled'] = function() {
return this.isLevelEnabled(level);
};
Logger.prototype[levelString.toLowerCase()] = function () {
if (this.isLevelEnabled(level)) {
var args = Array.prototype.slice.call(arguments);
args.unshift(level);
Logger.prototype.log.apply(this, args);
}
};
}
);
exports.LoggingEvent = LoggingEvent;
exports.Logger = Logger;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| console.js | 83.33% | (10 / 12) | 75% | (3 / 4) | 66.67% | (2 / 3) | 83.33% | (10 / 12) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 1 1 1 1 1 1 1 1 1 1 | "use strict";
var layouts = require('../layouts')
, consoleLog = console.log.bind(console);
function consoleAppender (layout) {
layout = layout || layouts.colouredLayout;
return function(loggingEvent) {
consoleLog(layout(loggingEvent));
};
}
function configure(config) {
var layout;
Iif (config.layout) {
layout = layouts.layout(config.layout.type, config.layout);
}
return consoleAppender(layout);
}
exports.appender = consoleAppender;
exports.configure = configure;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| gruntfile.js | 14.29% | (1 / 7) | 100% | (0 / 0) | 0% | (0 / 1) | 14.29% | (1 / 7) | |
| index.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 1 | 'use strict'; module.exports = function(grunt) { grunt.loadNpmTasks('grunt-mocha-test'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-jshint'); var src = ['test/manager/taskManager.js', 'test/filters/*.js', 'test/remote/*.js', 'test/service/*.js', 'test/modules/*.js', 'test/util/*.js', 'test/*.js']; // Project configuration. grunt.initConfig({ mochaTest: { test: { options: { reporter: 'spec', timeout: 5000, require: 'coverage/blanket' }, src: src }, coverage: { options: { reporter: 'html-cov', quiet: true, captureFile: 'coverage.html' }, src: src } }, clean: { "coverage.html" : { src: ['coverage.html'] } }, jshint: { all: ['lib/*'] } }); // Default task. grunt.registerTask('default', ['clean', 'mochaTest', 'jshint']); }; |
| 1 2 | 1 | module.exports = require('./lib/pomelo');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| application.js | 27.25% | (103 / 378) | 6.71% | (11 / 164) | 9.09% | (6 / 66) | 27.25% | (103 / 378) | |
| pomelo.js | 88% | (44 / 50) | 62.5% | (5 / 8) | 83.33% | (5 / 6) | 88% | (44 / 50) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 8 1 1 1 1 1 1 1 1 1 1 1 1 1 4 4 4 4 4 4 4 4 4 1 1 1 1 1 1 1 6 6 6 4 6 1 16 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- proto
* Copyright(c) 2012 xiechengchao <xiecc@163.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('./util/utils');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var EventEmitter = require('events').EventEmitter;
var events = require('./util/events');
var appUtil = require('./util/appUtil');
var Constants = require('./util/constants');
var appManager = require('./common/manager/appManager');
var fs = require('fs');
var path = require('path');
/**
* Application prototype.
*
* @module
*/
var Application = module.exports = {};
/**
* Application states
*/
var STATE_INITED = 1; // app has inited
var STATE_START = 2; // app start
var STATE_STARTED = 3; // app has started
var STATE_STOPED = 4; // app has stoped
/**
* Initialize the server.
*
* - setup default configuration
*/
Application.init = function(opts) {
opts = opts || {};
this.loaded = []; // loaded component list
this.components = {}; // name -> component map
this.settings = {}; // collection keep set/get
var base = opts.base || path.dirname(require.main.filename);
this.set(Constants.RESERVED.BASE, base, true);
this.event = new EventEmitter(); // event object to sub/pub events
// current server info
this.serverId = null; // current server id
this.serverType = null; // current server type
this.curServer = null; // current server info
this.startTime = null; // current server start time
// global server infos
this.master = null; // master server info
this.servers = {}; // current global server info maps, id -> info
this.serverTypeMaps = {}; // current global type maps, type -> [info]
this.serverTypes = []; // current global server type list
this.lifecycleCbs = {}; // current server custom lifecycle callbacks
this.clusterSeq = {}; // cluster id seqence
appUtil.defaultConfiguration(this);
this.state = STATE_INITED;
logger.info('application inited: %j', this.getServerId());
};
/**
* Get application base path
*
* // cwd: /home/game/
* pomelo start
* // app.getBase() -> /home/game
*
* @return {String} application base path
*
* @memberOf Application
*/
Application.getBase = function() {
return this.get(Constants.RESERVED.BASE);
};
/**
* Override require method in application
*
* @param {String} relative path of file
*
* @memberOf Application
*/
Application.require = function(ph) {
return require(path.join(Application.getBase(), ph));
};
/**
* Configure logger with {$base}/config/log4js.json
*
* @param {Object} logger pomelo-logger instance without configuration
*
* @memberOf Application
*/
Application.configureLogger = function(logger) {
if (process.env.POMELO_LOGGER !== 'off') {
var base = this.getBase();
var env = this.get(Constants.RESERVED.ENV);
var originPath = path.join(base, Constants.FILEPATH.LOG);
var presentPath = path.join(base, Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG));
if(fs.existsSync(originPath)) {
logger.configure(originPath, {serverId: this.serverId, base: base});
} else if(fs.existsSync(presentPath)) {
logger.configure(presentPath, {serverId: this.serverId, base: base});
} else {
logger.error('logger file path configuration is error.');
}
}
};
/**
* add a filter to before and after filter
*
* @param {Object} filter provide before and after filter method.
* A filter should have two methods: before and after.
* @memberOf Application
*/
Application.filter = function (filter) {
this.before(filter);
this.after(filter);
};
/**
* Add before filter.
*
* @param {Object|Function} bf before fileter, bf(msg, session, next)
* @memberOf Application
*/
Application.before = function (bf) {
addFilter(this, Constants.KEYWORDS.BEFORE_FILTER, bf);
};
/**
* Add after filter.
*
* @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`
* @memberOf Application
*/
Application.after = function (af) {
addFilter(this, Constants.KEYWORDS.AFTER_FILTER, af);
};
/**
* add a global filter to before and after global filter
*
* @param {Object} filter provide before and after filter method.
* A filter should have two methods: before and after.
* @memberOf Application
*/
Application.globalFilter = function (filter) {
this.globalBefore(filter);
this.globalAfter(filter);
};
/**
* Add global before filter.
*
* @param {Object|Function} bf before fileter, bf(msg, session, next)
* @memberOf Application
*/
Application.globalBefore = function (bf) {
addFilter(this, Constants.KEYWORDS.GLOBAL_BEFORE_FILTER, bf);
};
/**
* Add global after filter.
*
* @param {Object|Function} af after filter, `af(err, msg, session, resp, next)`
* @memberOf Application
*/
Application.globalAfter = function (af) {
addFilter(this, Constants.KEYWORDS.GLOBAL_AFTER_FILTER, af);
};
/**
* Add rpc before filter.
*
* @param {Object|Function} bf before fileter, bf(serverId, msg, opts, next)
* @memberOf Application
*/
Application.rpcBefore = function(bf) {
addFilter(this, Constants.KEYWORDS.RPC_BEFORE_FILTER, bf);
};
/**
* Add rpc after filter.
*
* @param {Object|Function} af after filter, `af(serverId, msg, opts, next)`
* @memberOf Application
*/
Application.rpcAfter = function(af) {
addFilter(this, Constants.KEYWORDS.RPC_AFTER_FILTER, af);
};
/**
* add a rpc filter to before and after rpc filter
*
* @param {Object} filter provide before and after filter method.
* A filter should have two methods: before and after.
* @memberOf Application
*/
Application.rpcFilter = function(filter) {
this.rpcBefore(filter);
this.rpcAfter(filter);
};
/**
* Load component
*
* @param {String} name (optional) name of the component
* @param {Object} component component instance or factory function of the component
* @param {[type]} opts (optional) construct parameters for the factory function
* @return {Object} app instance for chain invoke
* @memberOf Application
*/
Application.load = function(name, component, opts) {
if(typeof name !== 'string') {
opts = component;
component = name;
name = null;
if(typeof component.name === 'string') {
name = component.name;
}
}
if(typeof component === 'function') {
component = component(this, opts);
}
if(!name && typeof component.name === 'string') {
name = component.name;
}
if(name && this.components[name]) {
// ignore duplicat component
logger.warn('ignore duplicate component: %j', name);
return;
}
this.loaded.push(component);
if(name) {
// components with a name would get by name throught app.components later.
this.components[name] = component;
}
return this;
};
/**
* Load Configure json file to settings.(support different enviroment directory & compatible for old path)
*
* @param {String} key environment key
* @param {String} val environment value
* @param {Boolean} reload whether reload after change default false
* @return {Server|Mixed} for chaining, or the setting value
* @memberOf Application
*/
Application.loadConfigBaseApp = function (key, val, reload) {
var self = this;
var env = this.get(Constants.RESERVED.ENV);
var originPath = path.join(Application.getBase(), val);
var presentPath = path.join(Application.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(val));
var realPath;
Iif(fs.existsSync(originPath)) {
realPath = originPath;
var file = require(originPath);
if (file[env]) {
file = file[env];
}
this.set(key, file);
} else Iif(fs.existsSync(presentPath)) {
realPath = presentPath;
var pfile = require(presentPath);
this.set(key, pfile);
} else {
logger.error('invalid configuration with file path: %s', key);
}
Iif(!!realPath && !!reload) {
fs.watch(realPath, function (event, filename) {
if(event === 'change') {
delete require.cache[require.resolve(realPath)];
self.loadConfigBaseApp(key, val);
}
});
}
};
/**
* Load Configure json file to settings.
*
* @param {String} key environment key
* @param {String} val environment value
* @return {Server|Mixed} for chaining, or the setting value
* @memberOf Application
*/
Application.loadConfig = function(key, val) {
var env = this.get(Constants.RESERVED.ENV);
val = require(val);
if (val[env]) {
val = val[env];
}
this.set(key, val);
};
/**
* Set the route function for the specified server type.
*
* Examples:
*
* app.route('area', routeFunc);
*
* var routeFunc = function(session, msg, app, cb) {
* // all request to area would be route to the first area server
* var areas = app.getServersByType('area');
* cb(null, areas[0].id);
* };
*
* @param {String} serverType server type string
* @param {Function} routeFunc route function. routeFunc(session, msg, app, cb)
* @return {Object} current application instance for chain invoking
* @memberOf Application
*/
Application.route = function(serverType, routeFunc) {
var routes = this.get(Constants.KEYWORDS.ROUTE);
if(!routes) {
routes = {};
this.set(Constants.KEYWORDS.ROUTE, routes);
}
routes[serverType] = routeFunc;
return this;
};
/**
* Set before stop function. It would perform before servers stop.
*
* @param {Function} fun before close function
* @return {Void}
* @memberOf Application
*/
Application.beforeStopHook = function(fun) {
logger.warn('this method was deprecated in pomelo 0.8');
if(!!fun && typeof fun === 'function') {
this.set(Constants.KEYWORDS.BEFORE_STOP_HOOK, fun);
}
};
/**
* Start application. It would load the default components and start all the loaded components.
*
* @param {Function} cb callback function
* @memberOf Application
*/
Application.start = function(cb) {
this.startTime = Date.now();
if(this.state > STATE_INITED) {
utils.invokeCallback(cb, new Error('application has already start.'));
return;
}
var self = this;
appUtil.startByType(self, function() {
appUtil.loadDefaultComponents(self);
var startUp = function() {
appUtil.optComponents(self.loaded, Constants.RESERVED.START, function(err) {
self.state = STATE_START;
if(err) {
utils.invokeCallback(cb, err);
} else {
logger.info('%j enter after start...', self.getServerId());
self.afterStart(cb);
}
});
};
var beforeFun = self.lifecycleCbs[Constants.LIFECYCLE.BEFORE_STARTUP];
if(!!beforeFun) {
beforeFun.call(null, self, startUp);
} else {
startUp();
}
});
};
/**
* Lifecycle callback for after start.
*
* @param {Function} cb callback function
* @return {Void}
*/
Application.afterStart = function(cb) {
if(this.state !== STATE_START) {
utils.invokeCallback(cb, new Error('application is not running now.'));
return;
}
var afterFun = this.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTUP];
var self = this;
appUtil.optComponents(this.loaded, Constants.RESERVED.AFTER_START, function(err) {
self.state = STATE_STARTED;
var id = self.getServerId();
if(!err) {
logger.info('%j finish start', id);
}
if(!!afterFun) {
afterFun.call(null, self, function() {
utils.invokeCallback(cb, err);
});
} else {
utils.invokeCallback(cb, err);
}
var usedTime = Date.now() - self.startTime;
logger.info('%j startup in %s ms', id, usedTime);
self.event.emit(events.START_SERVER, id);
});
};
/**
* Stop components.
*
* @param {Boolean} force whether stop the app immediately
*/
Application.stop = function(force) {
if(this.state > STATE_STARTED) {
logger.warn('[pomelo application] application is not running now.');
return;
}
this.state = STATE_STOPED;
var self = this;
this.stopTimer = setTimeout(function() {
process.exit(0);
}, Constants.TIME.TIME_WAIT_STOP);
var cancelShutDownTimer =function(){
if(!!self.stopTimer) {
clearTimeout(self.stopTimer);
}
};
var shutDown = function() {
appUtil.stopComps(self.loaded, 0, force, function() {
cancelShutDownTimer();
if(force) {
process.exit(0);
}
});
};
var fun = this.get(Constants.KEYWORDS.BEFORE_STOP_HOOK);
var stopFun = this.lifecycleCbs[Constants.LIFECYCLE.BEFORE_SHUTDOWN];
if(!!stopFun) {
stopFun.call(null, this, shutDown, cancelShutDownTimer);
} else if(!!fun) {
utils.invokeCallback(fun, self, shutDown, cancelShutDownTimer);
} else {
shutDown();
}
};
/**
* Assign `setting` to `val`, or return `setting`'s value.
*
* Example:
*
* app.set('key1', 'value1');
* app.get('key1'); // 'value1'
* app.key1; // undefined
*
* app.set('key2', 'value2', true);
* app.get('key2'); // 'value2'
* app.key2; // 'value2'
*
* @param {String} setting the setting of application
* @param {String} val the setting's value
* @param {Boolean} attach whether attach the settings to application
* @return {Server|Mixed} for chaining, or the setting value
* @memberOf Application
*/
Application.set = function (setting, val, attach) {
Iif (arguments.length === 1) {
return this.settings[setting];
}
this.settings[setting] = val;
if(attach) {
this[setting] = val;
}
return this;
};
/**
* Get property from setting
*
* @param {String} setting application setting
* @return {String} val
* @memberOf Application
*/
Application.get = function (setting) {
return this.settings[setting];
};
/**
* Check if `setting` is enabled.
*
* @param {String} setting application setting
* @return {Boolean}
* @memberOf Application
*/
Application.enabled = function (setting) {
return !!this.get(setting);
};
/**
* Check if `setting` is disabled.
*
* @param {String} setting application setting
* @return {Boolean}
* @memberOf Application
*/
Application.disabled = function (setting) {
return !this.get(setting);
};
/**
* Enable `setting`.
*
* @param {String} setting application setting
* @return {app} for chaining
* @memberOf Application
*/
Application.enable = function (setting) {
return this.set(setting, true);
};
/**
* Disable `setting`.
*
* @param {String} setting application setting
* @return {app} for chaining
* @memberOf Application
*/
Application.disable = function (setting) {
return this.set(setting, false);
};
/**
* Configure callback for the specified env and server type.
* When no env is specified that callback will
* be invoked for all environments and when no type is specified
* that callback will be invoked for all server types.
*
* Examples:
*
* app.configure(function(){
* // executed for all envs and server types
* });
*
* app.configure('development', function(){
* // executed development env
* });
*
* app.configure('development', 'connector', function(){
* // executed for development env and connector server type
* });
*
* @param {String} env application environment
* @param {Function} fn callback function
* @param {String} type server type
* @return {Application} for chaining
* @memberOf Application
*/
Application.configure = function (env, type, fn) {
var args = [].slice.call(arguments);
fn = args.pop();
env = type = Constants.RESERVED.ALL;
if(args.length > 0) {
env = args[0];
}
if(args.length > 1) {
type = args[1];
}
if (env === Constants.RESERVED.ALL || contains(this.settings.env, env)) {
if (type === Constants.RESERVED.ALL || contains(this.settings.serverType, type)) {
fn.call(this);
}
}
return this;
};
/**
* Register admin modules. Admin modules is the extends point of the monitor system.
*
* @param {String} module (optional) module id or provoided by module.moduleId
* @param {Object} module module object or factory function for module
* @param {Object} opts construct parameter for module
* @memberOf Application
*/
Application.registerAdmin = function(moduleId, module, opts) {
var modules = this.get(Constants.KEYWORDS.MODULE);
if(!modules) {
modules = {};
this.set(Constants.KEYWORDS.MODULE, modules);
}
if(typeof moduleId !== 'string') {
opts = module;
module = moduleId;
if(module) {
moduleId = module.moduleId;
}
}
if(!moduleId){
return;
}
modules[moduleId] = {
moduleId: moduleId,
module: module,
opts: opts
};
};
/**
* Use plugin.
*
* @param {Object} plugin plugin instance
* @param {[type]} opts (optional) construct parameters for the factory function
* @memberOf Application
*/
Application.use = function(plugin, opts) {
if(!plugin.components) {
logger.error('invalid components, no components exist');
return;
}
var self = this;
opts = opts || {};
var dir = path.dirname(plugin.components);
if(!fs.existsSync(plugin.components)) {
logger.error('fail to find components, find path: %s', plugin.components);
return;
}
fs.readdirSync(plugin.components).forEach(function (filename) {
if (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
var param = opts[name] || {};
var absolutePath = path.join(dir, Constants.DIR.COMPONENT, filename);
if(!fs.existsSync(absolutePath)) {
logger.error('component %s not exist at %s', name, absolutePath);
} else {
self.load(require(absolutePath), param);
}
});
// load events
if(!plugin.events) {
return;
} else {
if(!fs.existsSync(plugin.events)) {
logger.error('fail to find events, find path: %s', plugin.events);
return;
}
fs.readdirSync(plugin.events).forEach(function (filename) {
if (!/\.js$/.test(filename)) {
return;
}
var absolutePath = path.join(dir, Constants.DIR.EVENT, filename);
if(!fs.existsSync(absolutePath)) {
logger.error('events %s not exist at %s', filename, absolutePath);
} else {
bindEvents(require(absolutePath), self);
}
});
}
};
/**
* Application transaction. Transcation includes conditions and handlers, if conditions are satisfied, handlers would be executed.
* And you can set retry times to execute handlers. The transaction log is in file logs/transaction.log.
*
* @param {String} name transaction name
* @param {Object} conditions functions which are called before transaction
* @param {Object} handlers functions which are called during transaction
* @param {Number} retry retry times to execute handlers if conditions are successfully executed
* @memberOf Application
*/
Application.transaction = function(name, conditions, handlers, retry) {
appManager.transaction(name, conditions, handlers, retry);
};
/**
* Get master server info.
*
* @return {Object} master server info, {id, host, port}
* @memberOf Application
*/
Application.getMaster = function() {
return this.master;
};
/**
* Get current server info.
*
* @return {Object} current server info, {id, serverType, host, port}
* @memberOf Application
*/
Application.getCurServer = function() {
return this.curServer;
};
/**
* Get current server id.
*
* @return {String|Number} current server id from servers.json
* @memberOf Application
*/
Application.getServerId = function() {
return this.serverId;
};
/**
* Get current server type.
*
* @return {String|Number} current server type from servers.json
* @memberOf Application
*/
Application.getServerType = function() {
return this.serverType;
};
/**
* Get all the current server infos.
*
* @return {Object} server info map, key: server id, value: server info
* @memberOf Application
*/
Application.getServers = function() {
return this.servers;
};
/**
* Get all server infos from servers.json.
*
* @return {Object} server info map, key: server id, value: server info
* @memberOf Application
*/
Application.getServersFromConfig = function() {
return this.get(Constants.KEYWORDS.SERVER_MAP);
};
/**
* Get all the server type.
*
* @return {Array} server type list
* @memberOf Application
*/
Application.getServerTypes = function() {
return this.serverTypes;
};
/**
* Get server info by server id from current server cluster.
*
* @param {String} serverId server id
* @return {Object} server info or undefined
* @memberOf Application
*/
Application.getServerById = function(serverId) {
return this.servers[serverId];
};
/**
* Get server info by server id from servers.json.
*
* @param {String} serverId server id
* @return {Object} server info or undefined
* @memberOf Application
*/
Application.getServerFromConfig = function(serverId) {
return this.get(Constants.KEYWORDS.SERVER_MAP)[serverId];
};
/**
* Get server infos by server type.
*
* @param {String} serverType server type
* @return {Array} server info list
* @memberOf Application
*/
Application.getServersByType = function(serverType) {
return this.serverTypeMaps[serverType];
};
/**
* Check the server whether is a frontend server
*
* @param {server} server server info. it would check current server
* if server not specified
* @return {Boolean}
*
* @memberOf Application
*/
Application.isFrontend = function(server) {
server = server || this.getCurServer();
return !!server && server.frontend === 'true';
};
/**
* Check the server whether is a backend server
*
* @param {server} server server info. it would check current server
* if server not specified
* @return {Boolean}
* @memberOf Application
*/
Application.isBackend = function(server) {
server = server || this.getCurServer();
return !!server && !server.frontend;
};
/**
* Check whether current server is a master server
*
* @return {Boolean}
* @memberOf Application
*/
Application.isMaster = function() {
return this.serverType === Constants.RESERVED.MASTER;
};
/**
* Add new server info to current application in runtime.
*
* @param {Array} servers new server info list
* @memberOf Application
*/
Application.addServers = function(servers) {
if(!servers || !servers.length) {
return;
}
var item, slist;
for(var i=0, l=servers.length; i<l; i++) {
item = servers[i];
// update global server map
this.servers[item.id] = item;
// update global server type map
slist = this.serverTypeMaps[item.serverType];
if(!slist) {
this.serverTypeMaps[item.serverType] = slist = [];
}
replaceServer(slist, item);
// update global server type list
if(this.serverTypes.indexOf(item.serverType) < 0) {
this.serverTypes.push(item.serverType);
}
}
this.event.emit(events.ADD_SERVERS, servers);
};
/**
* Remove server info from current application at runtime.
*
* @param {Array} ids server id list
* @memberOf Application
*/
Application.removeServers = function(ids) {
if(!ids || !ids.length) {
return;
}
var id, item, slist;
for(var i=0, l=ids.length; i<l; i++) {
id = ids[i];
item = this.servers[id];
if(!item) {
continue;
}
// clean global server map
delete this.servers[id];
// clean global server type map
slist = this.serverTypeMaps[item.serverType];
removeServer(slist, id);
// TODO: should remove the server type if the slist is empty?
}
this.event.emit(events.REMOVE_SERVERS, ids);
};
/**
* Replace server info from current application at runtime.
*
* @param {Object} server id map
* @memberOf Application
*/
Application.replaceServers = function(servers) {
if(!servers){
return;
}
this.servers = servers;
this.serverTypeMaps = {};
this.serverTypes = [];
var serverArray = [];
for(var id in servers){
var server = servers[id];
var serverType = server[Constants.RESERVED.SERVER_TYPE];
var slist = this.serverTypeMaps[serverType];
if(!slist) {
this.serverTypeMaps[serverType] = slist = [];
}
this.serverTypeMaps[serverType].push(server);
// update global server type list
if(this.serverTypes.indexOf(serverType) < 0) {
this.serverTypes.push(serverType);
}
serverArray.push(server);
}
this.event.emit(events.REPLACE_SERVERS, serverArray);
};
/**
* Add crons from current application at runtime.
*
* @param {Array} crons new crons would be added in application
* @memberOf Application
*/
Application.addCrons = function(crons) {
if(!crons || !crons.length) {
logger.warn('crons is not defined.');
return;
}
this.event.emit(events.ADD_CRONS, crons);
};
/**
* Remove crons from current application at runtime.
*
* @param {Array} crons old crons would be removed in application
* @memberOf Application
*/
Application.removeCrons = function(crons) {
if(!crons || !crons.length) {
logger.warn('ids is not defined.');
return;
}
this.event.emit(events.REMOVE_CRONS, crons);
};
var replaceServer = function(slist, serverInfo) {
for(var i=0, l=slist.length; i<l; i++) {
if(slist[i].id === serverInfo.id) {
slist[i] = serverInfo;
return;
}
}
slist.push(serverInfo);
};
var removeServer = function(slist, id) {
if(!slist || !slist.length) {
return;
}
for(var i=0, l=slist.length; i<l; i++) {
if(slist[i].id === id) {
slist.splice(i, 1);
return;
}
}
};
var contains = function(str, settings) {
if(!settings) {
return false;
}
var ts = settings.split("|");
for(var i=0, l=ts.length; i<l; i++) {
if(str === ts[i]) {
return true;
}
}
return false;
};
var bindEvents = function(Event, app) {
var emethods = new Event(app);
for(var m in emethods) {
if(typeof emethods[m] === 'function') {
app.event.on(m, emethods[m].bind(emethods));
}
}
};
var addFilter = function(app, type, filter) {
var filters = app.get(type);
if(!filters) {
filters = [];
app.set(type, filters);
}
filters.push(filter);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 13 13 13 13 13 1 4 4 4 4 4 1 2 2 2 2 1 428 340 88 | /*!
* Pomelo
* Copyright(c) 2012 xiechengchao <xiecc@163.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var fs = require('fs');
var path = require('path');
var application = require('./application');
var Package = require('../package');
/**
* Expose `createApplication()`.
*
* @module
*/
var Pomelo = module.exports = {};
/**
* Framework version.
*/
Pomelo.version = Package.version;
/**
* Event definitions that would be emitted by app.event
*/
Pomelo.events = require('./util/events');
/**
* auto loaded components
*/
Pomelo.components = {};
/**
* auto loaded filters
*/
Pomelo.filters = {};
/**
* auto loaded rpc filters
*/
Pomelo.rpcFilters = {};
/**
* connectors
*/
Pomelo.connectors = {};
Pomelo.connectors.__defineGetter__('sioconnector', load.bind(null, './connectors/sioconnector'));
Pomelo.connectors.__defineGetter__('hybridconnector', load.bind(null, './connectors/hybridconnector'));
Pomelo.connectors.__defineGetter__('udpconnector', load.bind(null, './connectors/udpconnector'));
Pomelo.connectors.__defineGetter__('mqttconnector', load.bind(null, './connectors/mqttconnector'));
/**
* pushSchedulers
*/
Pomelo.pushSchedulers = {};
Pomelo.pushSchedulers.__defineGetter__('direct', load.bind(null, './pushSchedulers/direct'));
Pomelo.pushSchedulers.__defineGetter__('buffer', load.bind(null, './pushSchedulers/buffer'));
var self = this;
/**
* Create an pomelo application.
*
* @return {Application}
* @memberOf Pomelo
* @api public
*/
Pomelo.createApp = function (opts) {
var app = application;
app.init(opts);
self.app = app;
return app;
};
/**
* Get application
*/
Object.defineProperty(Pomelo, 'app', {
get:function () {
return self.app;
}
});
/**
* Auto-load bundled components with getters.
*/
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
Iif (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
var _load = load.bind(null, './components/', name);
Pomelo.components.__defineGetter__(name, _load);
Pomelo.__defineGetter__(name, _load);
});
fs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) {
Iif (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
var _load = load.bind(null, './filters/handler/', name);
Pomelo.filters.__defineGetter__(name, _load);
Pomelo.__defineGetter__(name, _load);
});
fs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) {
Iif (!/\.js$/.test(filename)) {
return;
}
var name = path.basename(filename, '.js');
var _load = load.bind(null, './filters/rpc/', name);
Pomelo.rpcFilters.__defineGetter__(name, _load);
});
function load(path, name) {
if (name) {
return require(path + name);
}
return require(path);
}
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| appManager.js | 11.11% | (7 / 63) | 0% | (0 / 24) | 0% | (0 / 12) | 11.11% | (7 / 63) | |
| taskManager.js | 40% | (6 / 15) | 0% | (0 / 4) | 0% | (0 / 2) | 40% | (6 / 15) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 1 1 1 1 1 1 1 | var async = require('async');
var utils = require('../../util/utils');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var transactionLogger = require('pomelo-logger').getLogger('transaction-log', __filename);
var transactionErrorLogger = require('pomelo-logger').getLogger('transaction-error-log', __filename);
var manager = module.exports;
manager.transaction = function(name, conditions, handlers, retry) {
if(!retry) {
retry = 1;
}
if(typeof name !== 'string') {
logger.error('transaction name is error format, name: %s.', name);
return;
}
if(typeof conditions !== 'object' || typeof handlers !== 'object') {
logger.error('transaction conditions parameter is error format, conditions: %j, handlers: %j.', conditions, handlers);
return;
}
var cmethods=[] ,dmethods=[], cnames=[], dnames=[];
for(var key in conditions) {
if(typeof key !== 'string' || typeof conditions[key] !== 'function') {
logger.error('transaction conditions parameter is error format, condition name: %s, condition function: %j.', key, conditions[key]);
return;
}
cnames.push(key);
cmethods.push(conditions[key]);
}
var i = 0;
// execute conditions
async.forEachSeries(cmethods, function(method, cb) {
method(cb);
transactionLogger.info('[%s]:[%s] condition is executed.', name, cnames[i]);
i++;
}, function(err) {
if(err) {
process.nextTick(function() {
transactionLogger.error('[%s]:[%s] condition is executed with err: %j.', name, cnames[--i], err.stack);
var log = {
name: name,
method: cnames[i],
time: Date.now(),
type: 'condition',
description: err.stack
};
transactionErrorLogger.error(JSON.stringify(log));
});
return;
} else {
// execute handlers
process.nextTick(function() {
for(var key in handlers) {
if(typeof key !== 'string' || typeof handlers[key] !== 'function') {
logger.error('transcation handlers parameter is error format, handler name: %s, handler function: %j.', key, handlers[key]);
return;
}
dnames.push(key);
dmethods.push(handlers[key]);
}
var flag = true;
var times = retry;
// do retry if failed util retry times
async.whilst(
function() {
return retry > 0 && flag;
},
function(callback) {
var j = 0;
retry--;
async.forEachSeries(dmethods, function(method, cb) {
method(cb);
transactionLogger.info('[%s]:[%s] handler is executed.', name, dnames[j]);
j++;
}, function(err) {
if(err) {
process.nextTick(function() {
transactionLogger.error('[%s]:[%s]:[%s] handler is executed with err: %j.', name, dnames[--j], times-retry, err.stack);
var log = {
name: name,
method: dnames[j],
retry: times-retry,
time: Date.now(),
type: 'handler',
description: err.stack
};
transactionErrorLogger.error(JSON.stringify(log));
utils.invokeCallback(callback);
});
return;
}
flag = false;
utils.invokeCallback(callback);
process.nextTick(function() {
transactionLogger.info('[%s] all conditions and handlers are executed successfully.', name);
});
});
},
function(err) {
if(err) {
logger.error('transaction process is executed with error: %j', err);
}
// callback will not pass error
}
);
});
}
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 1 1 1 1 1 1 | var sequeue = require('seq-queue');
var manager = module.exports;
var queues = {};
manager.timeout = 3000;
/**
* Add tasks into task group. Create the task group if it dose not exist.
*
* @param {String} key task key
* @param {Function} fn task callback
* @param {Function} ontimeout task timeout callback
* @param {Number} timeout timeout for task
*/
manager.addTask = function(key, fn, ontimeout, timeout) {
var queue = queues[key];
if(!queue) {
queue = sequeue.createQueue(manager.timeout);
queues[key] = queue;
}
return queue.push(fn, ontimeout, timeout);
};
/**
* Destroy task group
*
* @param {String} key task key
* @param {Boolean} force whether close task group directly
*/
manager.closeQueue = function(key, force) {
if(!queues[key]) {
// ignore illeagle key
return;
}
queues[key].close(force);
delete queues[key];
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| channelRemote.js | 23.08% | (6 / 26) | 0% | (0 / 4) | 0% | (0 / 5) | 23.08% | (6 / 26) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 1 1 1 1 1 1 | /**
* Remote channel service for frontend server.
* Receive push request from backend servers and push it to clients.
*/
var utils = require('../../../util/utils');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
module.exports = function(app) {
return new Remote(app);
};
var Remote = function(app) {
this.app = app;
};
/**
* Push message to client by uids.
*
* @param {String} route route string of message
* @param {Object} msg message
* @param {Array} uids user ids that would receive the message
* @param {Object} opts push options
* @param {Function} cb callback function
*/
Remote.prototype.pushMessage = function(route, msg, uids, opts, cb) {
if(!msg){
logger.error('Can not send empty message! route : %j, compressed msg : %j',
route, msg);
utils.invokeCallback(cb, new Error('can not send empty message.'));
return;
}
var connector = this.app.components.__connector__;
var sessionService = this.app.get('sessionService');
var fails = [], sids = [], sessions, j, k;
for(var i=0, l=uids.length; i<l; i++) {
sessions = sessionService.getByUid(uids[i]);
if(!sessions) {
fails.push(uids[i]);
} else {
for(j=0, k=sessions.length; j<k; j++) {
sids.push(sessions[j].id);
}
}
}
logger.debug('[%s] pushMessage uids: %j, msg: %j, sids: %j', this.app.serverId, uids, msg, sids);
connector.send(null, route, msg, sids, opts, function(err) {
utils.invokeCallback(cb, err, fails);
});
};
/**
* Broadcast to all the client connectd with current frontend server.
*
* @param {String} route route string
* @param {Object} msg message
* @param {Boolean} opts broadcast options.
* @param {Function} cb callback function
*/
Remote.prototype.broadcast = function(route, msg, opts, cb) {
var connector = this.app.components.__connector__;
connector.send(null, route, msg, null, opts, cb);
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| backendSessionService.js | 21.5% | (23 / 107) | 0% | (0 / 16) | 0% | (0 / 23) | 21.5% | (23 / 107) | |
| channelService.js | 12.36% | (32 / 259) | 0% | (0 / 111) | 0% | (0 / 43) | 12.4% | (32 / 258) | |
| connectionService.js | 25.71% | (9 / 35) | 0% | (0 / 14) | 0% | (0 / 7) | 25.71% | (9 / 35) | |
| filterService.js | 23.68% | (9 / 38) | 0% | (0 / 14) | 0% | (0 / 7) | 23.68% | (9 / 38) | |
| handlerService.js | 19.72% | (14 / 71) | 0% | (0 / 32) | 0% | (0 / 8) | 19.72% | (14 / 71) | |
| sessionService.js | 20.33% | (50 / 246) | 0% | (0 / 66) | 0% | (0 / 53) | 20.33% | (50 / 246) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* backend session service for backend session
*/
var utils = require('../../util/utils');
var EXPORTED_FIELDS = ['id', 'frontendId', 'uid', 'settings'];
/**
* Service that maintains backend sessions and the communication with frontend
* servers.
*
* BackendSessionService would be created in each server process and maintains
* backend sessions for current process and communicates with the relative
* frontend servers.
*
* BackendSessionService instance could be accessed by
* `app.get('backendSessionService')` or app.backendSessionService.
*
* @class
* @constructor
*/
var BackendSessionService = function(app) {
this.app = app;
};
module.exports = BackendSessionService;
BackendSessionService.prototype.create = function(opts) {
if(!opts) {
throw new Error('opts should not be empty.');
}
return new BackendSession(opts, this);
};
/**
* Get backend session by frontend server id and session id.
*
* @param {String} frontendId frontend server id that session attached
* @param {String} sid session id
* @param {Function} cb callback function. args: cb(err, BackendSession)
*
* @memberOf BackendSessionService
*/
BackendSessionService.prototype.get = function(frontendId, sid, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'getBackendSessionBySid';
var args = [sid];
rpcInvoke(this.app, frontendId, namespace, service, method,
args, BackendSessionCB.bind(null, this, cb));
};
/**
* Get backend sessions by frontend server id and user id.
*
* @param {String} frontendId frontend server id that session attached
* @param {String} uid user id binded with the session
* @param {Function} cb callback function. args: cb(err, BackendSessions)
*
* @memberOf BackendSessionService
*/
BackendSessionService.prototype.getByUid = function(frontendId, uid, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'getBackendSessionsByUid';
var args = [uid];
rpcInvoke(this.app, frontendId, namespace, service, method,
args, BackendSessionCB.bind(null, this, cb));
};
/**
* Kick a session by session id.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number} sid session id
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
*/
BackendSessionService.prototype.kickBySid = function(frontendId, sid, reason, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'kickBySid';
var args = [sid];
if(typeof reason === 'function') {
cb = reason;
}else{
args.push(reason);
}
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
/**
* Kick sessions by user id.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number|String} uid user id
* @param {String} reason kick reason
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
*/
BackendSessionService.prototype.kickByUid = function(frontendId, uid, reason, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'kickByUid';
var args = [uid];
if(typeof reason === 'function') {
cb = reason;
}else{
args.push(reason);
}
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
/**
* Bind the session with the specified user id. It would finally invoke the
* the sessionService.bind in the cooperating frontend server.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number} sid session id
* @param {String} uid user id
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
BackendSessionService.prototype.bind = function(frontendId, sid, uid, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'bind';
var args = [sid, uid];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
/**
* Unbind the session with the specified user id. It would finally invoke the
* the sessionService.unbind in the cooperating frontend server.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number} sid session id
* @param {String} uid user id
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
BackendSessionService.prototype.unbind = function(frontendId, sid, uid, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'unbind';
var args = [sid, uid];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
/**
* Push the specified customized change to the frontend internal session.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number} sid session id
* @param {String} key key in session that should be push
* @param {Object} value value in session, primitive js object
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
BackendSessionService.prototype.push = function(frontendId, sid, key, value, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'push';
var args = [sid, key, value];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
/**
* Push all the customized changes to the frontend internal session.
*
* @param {String} frontendId cooperating frontend server id
* @param {Number} sid session id
* @param {Object} settings key/values in session that should be push
* @param {Function} cb callback function
*
* @memberOf BackendSessionService
* @api private
*/
BackendSessionService.prototype.pushAll = function(frontendId, sid, settings, cb) {
var namespace = 'sys';
var service = 'sessionRemote';
var method = 'pushAll';
var args = [sid, settings];
rpcInvoke(this.app, frontendId, namespace, service, method, args, cb);
};
var rpcInvoke = function(app, sid, namespace, service, method, args, cb) {
app.rpcInvoke(sid, {namespace: namespace, service: service, method: method, args: args}, cb);
};
/**
* BackendSession is the proxy for the frontend internal session passed to handlers and
* it helps to keep the key/value pairs for the server locally.
* Internal session locates in frontend server and should not be accessed directly.
*
* The mainly operation on backend session should be read and any changes happen in backend
* session is local and would be discarded in next request. You have to push the
* changes to the frontend manually if necessary. Any push would overwrite the last push
* of the same key silently and the changes would be saw in next request.
* And you have to make sure the transaction outside if you would push the session
* concurrently in different processes.
*
* See the api below for more details.
*
* @class
* @constructor
*/
var BackendSession = function(opts, service) {
for(var f in opts) {
this[f] = opts[f];
}
this.__sessionService__ = service;
};
/**
* Bind current session with the user id. It would push the uid to frontend
* server and bind uid to the frontend internal session.
*
* @param {Number|String} uid user id
* @param {Function} cb callback function
*
* @memberOf BackendSession
*/
BackendSession.prototype.bind = function(uid, cb) {
var self = this;
this.__sessionService__.bind(this.frontendId, this.id, uid, function(err) {
if(!err) {
self.uid = uid;
}
utils.invokeCallback(cb, err);
});
};
/**
* Unbind current session with the user id. It would push the uid to frontend
* server and unbind uid from the frontend internal session.
*
* @param {Number|String} uid user id
* @param {Function} cb callback function
*
* @memberOf BackendSession
*/
BackendSession.prototype.unbind = function(uid, cb) {
var self = this;
this.__sessionService__.unbind(this.frontendId, this.id, uid, function(err) {
if(!err) {
self.uid = null;
}
utils.invokeCallback(cb, err);
});
};
/**
* Set the key/value into backend session.
*
* @param {String} key key
* @param {Object} value value
*/
BackendSession.prototype.set = function(key, value) {
this.settings[key] = value;
};
/**
* Get the value from backend session by key.
*
* @param {String} key key
* @return {Object} value
*/
BackendSession.prototype.get = function(key) {
return this.settings[key];
};
/**
* Push the key/value in backend session to the front internal session.
*
* @param {String} key key
* @param {Function} cb callback function
*/
BackendSession.prototype.push = function(key, cb) {
this.__sessionService__.push(this.frontendId, this.id, key, this.get(key), cb);
};
/**
* Push all the key/values in backend session to the frontend internal session.
*
* @param {Function} cb callback function
*/
BackendSession.prototype.pushAll = function(cb) {
this.__sessionService__.pushAll(this.frontendId, this.id, this.settings, cb);
};
/**
* Export the key/values for serialization.
*
* @api private
*/
BackendSession.prototype.export = function() {
var res = {};
EXPORTED_FIELDS.forEach(function(field) {
res[field] = this[field];
});
return res;
};
var BackendSessionCB = function(service, cb, err, sinfo) {
if(err) {
utils.invokeCallback(cb, err);
return;
}
if(!sinfo) {
utils.invokeCallback(cb);
return;
}
var sessions = [];
if(Array.isArray(sinfo)){
// #getByUid
for(var i = 0,k = sinfo.length;i<k;i++){
sessions.push(service.create(sinfo[i]));
}
}
else{
// #get
sessions = service.create(sinfo);
}
utils.invokeCallback(cb, null, sessions);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var countDownLatch = require('../../util/countDownLatch');
var utils = require('../../util/utils');
var ChannelRemote = require('../remote/frontend/channelRemote');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* constant
*/
var ST_INITED = 0;
var ST_DESTROYED = 1;
/**
* Create and maintain channels for server local.
*
* ChannelService is created by channel component which is a default loaded
* component of pomelo and channel service would be accessed by `app.get('channelService')`.
*
* @class
* @constructor
*/
var ChannelService = function(app, opts) {
opts = opts || {};
this.app = app;
this.channels = {};
this.prefix = opts.prefix;
this.store = opts.store;
this.broadcastFilter = opts.broadcastFilter;
this.channelRemote = new ChannelRemote(app);
};
module.exports = ChannelService;
ChannelService.prototype.start = function(cb) {
restoreChannel(this, cb);
};
/**
* Create channel with name.
*
* @param {String} name channel's name
* @memberOf ChannelService
*/
ChannelService.prototype.createChannel = function(name) {
if(this.channels[name]) {
return this.channels[name];
}
var c = new Channel(name, this);
addToStore(this, genKey(this), genKey(this, name));
this.channels[name] = c;
return c;
};
/**
* Get channel by name.
*
* @param {String} name channel's name
* @param {Boolean} create if true, create channel
* @return {Channel}
* @memberOf ChannelService
*/
ChannelService.prototype.getChannel = function(name, create) {
var channel = this.channels[name];
if(!channel && !!create) {
channel = this.channels[name] = new Channel(name, this);
addToStore(this, genKey(this), genKey(this, name));
}
return channel;
};
/**
* Destroy channel by name.
*
* @param {String} name channel name
* @memberOf ChannelService
*/
ChannelService.prototype.destroyChannel = function(name) {
delete this.channels[name];
removeFromStore(this, genKey(this), genKey(this, name));
removeAllFromStore(this, genKey(this, name));
};
/**
* Push message by uids.
* Group the uids by group. ignore any uid if sid not specified.
*
* @param {String} route message route
* @param {Object} msg message that would be sent to client
* @param {Array} uids the receiver info list, [{uid: userId, sid: frontendServerId}]
* @param {Object} opts user-defined push options, optional
* @param {Function} cb cb(err)
* @memberOf ChannelService
*/
ChannelService.prototype.pushMessageByUids = function(route, msg, uids, opts, cb) {
if(typeof route !== 'string') {
cb = opts;
opts = uids;
uids = msg;
msg = route;
route = msg.route;
}
if(!cb && typeof opts === 'function') {
cb = opts;
opts = {};
}
if(!uids || uids.length === 0) {
utils.invokeCallback(cb, new Error('uids should not be empty'));
return;
}
var groups = {}, record;
for(var i=0, l=uids.length; i<l; i++) {
record = uids[i];
add(record.uid, record.sid, groups);
}
sendMessageByGroup(this, route, msg, groups, opts, cb);
};
/**
* Broadcast message to all the connected clients.
*
* @param {String} stype frontend server type string
* @param {String} route route string
* @param {Object} msg message
* @param {Object} opts user-defined broadcast options, optional
* opts.binded: push to binded sessions or all the sessions
* opts.filterParam: parameters for broadcast filter.
* @param {Function} cb callback
* @memberOf ChannelService
*/
ChannelService.prototype.broadcast = function(stype, route, msg, opts, cb) {
var app = this.app;
var namespace = 'sys';
var service = 'channelRemote';
var method = 'broadcast';
var servers = app.getServersByType(stype);
if(!servers || servers.length === 0) {
// server list is empty
utils.invokeCallback(cb);
return;
}
var count = servers.length;
var successFlag = false;
var latch = countDownLatch.createCountDownLatch(count, function() {
if(!successFlag) {
utils.invokeCallback(cb, new Error('broadcast fails'));
return;
}
utils.invokeCallback(cb, null);
});
var genCB = function(serverId) {
return function(err) {
if(err) {
logger.error('[broadcast] fail to push message to serverId: ' + serverId + ', err:' + err.stack);
latch.done();
return;
}
successFlag = true;
latch.done();
};
};
var self = this;
var sendMessage = function(serverId) {
return (function() {
if(serverId === app.serverId) {
self.channelRemote[method](route, msg, opts, genCB());
} else {
app.rpcInvoke(serverId, {namespace: namespace, service: service,
method: method, args: [route, msg, opts]}, genCB(serverId));
}
}());
};
opts = {type: 'broadcast', userOptions: opts || {}};
// for compatiblity
opts.isBroadcast = true;
if(opts.userOptions) {
opts.binded = opts.userOptions.binded;
opts.filterParam = opts.userOptions.filterParam;
}
for(var i=0, l=count; i<l; i++) {
sendMessage(servers[i].id);
}
};
/**
* Channel maintains the receiver collection for a subject. You can
* add users into a channel and then broadcast message to them by channel.
*
* @class channel
* @constructor
*/
var Channel = function(name, service) {
this.name = name;
this.groups = {}; // group map for uids. key: sid, value: [uid]
this.records = {}; // member records. key: uid
this.__channelService__ = service;
this.state = ST_INITED;
this.userAmount =0;
};
/**
* Add user to channel.
*
* @param {Number} uid user id
* @param {String} sid frontend server id which user has connected to
*/
Channel.prototype.add = function(uid, sid) {
if(this.state > ST_INITED) {
return false;
} else {
var res = add(uid, sid, this.groups);
if(res) {
this.records[uid] = {sid: sid, uid: uid};
this.userAmount =this.userAmount+1;
}
addToStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));
return res;
}
};
/**
* Remove user from channel.
*
* @param {Number} uid user id
* @param {String} sid frontend server id which user has connected to.
* @return [Boolean] true if success or false if fail
*/
Channel.prototype.leave = function(uid, sid) {
if(!uid || !sid) {
return false;
}
var res = deleteFrom(uid, sid, this.groups[sid]);
if(res){
delete this.records[uid];
this.userAmount = this.userAmount-1;
}
if(this.userAmount<0) this.userAmount=0;//robust
removeFromStore(this.__channelService__, genKey(this.__channelService__, this.name), genValue(sid, uid));
if(this.groups[sid] && this.groups[sid].length === 0) {
delete this.groups[sid];
}
return res;
};
/**
* Get channel UserAmount in a channel.
*
* @return {number } channel member amount
*/
Channel.prototype.getUserAmount = function() {
return this.userAmount;
};
/**
* Get channel members.
*
* <b>Notice:</b> Heavy operation.
*
* @return {Array} channel member uid list
*/
Channel.prototype.getMembers = function() {
var res = [], groups = this.groups;
var group, i, l;
for(var sid in groups) {
group = groups[sid];
for(i=0, l=group.length; i<l; i++) {
res.push(group[i]);
}
}
return res;
};
/**
* Get Member info.
*
* @param {String} uid user id
* @return {Object} member info
*/
Channel.prototype.getMember = function(uid) {
return this.records[uid];
};
/**
* Destroy channel.
*/
Channel.prototype.destroy = function() {
this.state = ST_DESTROYED;
this.__channelService__.destroyChannel(this.name);
};
/**
* Push message to all the members in the channel
*
* @param {String} route message route
* @param {Object} msg message that would be sent to client
* @param {Object} opts user-defined push options, optional
* @param {Function} cb callback function
*/
Channel.prototype.pushMessage = function(route, msg, opts, cb) {
if(this.state !== ST_INITED) {
utils.invokeCallback(new Error('channel is not running now'));
return;
}
if(typeof route !== 'string') {
cb = opts;
opts = msg;
msg = route;
route = msg.route;
}
if(!cb && typeof opts === 'function') {
cb = opts;
opts = {};
}
sendMessageByGroup(this.__channelService__, route, msg, this.groups, opts, cb);
};
/**
* add uid and sid into group. ignore any uid that uid not specified.
*
* @param uid user id
* @param sid server id
* @param groups {Object} grouped uids, , key: sid, value: [uid]
*/
var add = function(uid, sid, groups) {
if(!sid) {
logger.warn('ignore uid %j for sid not specified.', uid);
return false;
}
var group = groups[sid];
if(!group) {
group = [];
groups[sid] = group;
}
group.push(uid);
return true;
};
/**
* delete element from array
*/
var deleteFrom = function(uid, sid, group) {
if(!uid || !sid || !group) {
return false;
}
for(var i=0, l=group.length; i<l; i++) {
if(group[i] === uid) {
group.splice(i, 1);
return true;
}
}
return false;
};
/**
* push message by group
*
* @param route {String} route route message
* @param msg {Object} message that would be sent to client
* @param groups {Object} grouped uids, , key: sid, value: [uid]
* @param opts {Object} push options
* @param cb {Function} cb(err)
*
* @api private
*/
var sendMessageByGroup = function(channelService, route, msg, groups, opts, cb) {
var app = channelService.app;
var namespace = 'sys';
var service = 'channelRemote';
var method = 'pushMessage';
var count = utils.size(groups);
var successFlag = false;
var failIds = [];
logger.debug('[%s] channelService sendMessageByGroup route: %s, msg: %j, groups: %j, opts: %j', app.serverId, route, msg, groups, opts);
if(count === 0) {
// group is empty
utils.invokeCallback(cb);
return;
}
var latch = countDownLatch.createCountDownLatch(count, function() {
if(!successFlag) {
utils.invokeCallback(cb, new Error('all uids push message fail'));
return;
}
utils.invokeCallback(cb, null, failIds);
});
var rpcCB = function(serverId) {
return function(err, fails) {
if(err) {
logger.error('[pushMessage] fail to dispatch msg to serverId: ' + serverId + ', err:' + err.stack);
latch.done();
return;
}
if(fails) {
failIds = failIds.concat(fails);
}
successFlag = true;
latch.done();
};
};
opts = {type: 'push', userOptions: opts || {}};
// for compatiblity
opts.isPush = true;
var sendMessage = function(sid) {
return (function() {
if(sid === app.serverId) {
channelService.channelRemote[method](route, msg, groups[sid], opts, rpcCB(sid));
} else {
app.rpcInvoke(sid, {namespace: namespace, service: service,
method: method, args: [route, msg, groups[sid], opts]}, rpcCB(sid));
}
})();
};
var group;
for(var sid in groups) {
group = groups[sid];
if(group && group.length > 0) {
sendMessage(sid);
} else {
// empty group
process.nextTick(rpcCB(sid));
}
}
};
var restoreChannel = function(self, cb) {
if(!self.store) {
utils.invokeCallback(cb);
return;
} else {
loadAllFromStore(self, genKey(self), function(err, list) {
if(!!err) {
utils.invokeCallback(cb, err);
return;
} else {
if(!list.length || !Array.isArray(list)) {
utils.invokeCallback(cb);
return;
}
var load = function(key) {
return (function() {
loadAllFromStore(self, key, function(err, items) {
for(var j=0; j<items.length; j++) {
var array = items[j].split(':');
var sid = array[0];
var uid = array[1];
var channel = self.channels[name];
var res = add(uid, sid, channel.groups);
if(res) {
channel.records[uid] = {sid: sid, uid: uid};
}
}
});
})();
};
for(var i=0; i<list.length; i++) {
var name = list[i].slice(genKey(self).length + 1);
self.channels[name] = new Channel(name, self);
load(list[i]);
}
utils.invokeCallback(cb);
}
});
}
};
var addToStore = function(self, key, value) {
if(!!self.store) {
self.store.add(key, value, function(err) {
if(!!err) {
logger.error('add key: %s value: %s to store, with err: %j', key, value, err.stack);
}
});
}
};
var removeFromStore = function(self, key, value) {
if(!!self.store) {
self.store.remove(key, value, function(err) {
if(!!err) {
logger.error('remove key: %s value: %s from store, with err: %j', key, value, err.stack);
}
});
}
};
var loadAllFromStore = function(self, key, cb) {
if(!!self.store) {
self.store.load(key, function(err, list) {
if(!!err) {
logger.error('load key: %s from store, with err: %j', key, err.stack);
utils.invokeCallback(cb, err);
} else {
utils.invokeCallback(cb, null, list);
}
});
}
};
var removeAllFromStore = function(self, key) {
if(!!self.store) {
self.store.removeAll(key, function(err) {
if(!!err) {
logger.error('remove key: %s all members from store, with err: %j', key, err.stack);
}
});
}
};
var genKey = function(self, name) {
if(!!name) {
return self.prefix + ':' + self.app.serverId + ':' + name;
} else {
return self.prefix + ':' + self.app.serverId;
}
};
var genValue = function(sid, uid) {
return sid + ':' + uid;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 1 1 1 1 1 1 1 1 1 | /** * connection statistics service * record connection, login count and list */ var Service = function(app) { this.serverId = app.getServerId(); this.connCount = 0; this.loginedCount = 0; this.logined = {}; }; module.exports = Service; var pro = Service.prototype; /** * Add logined user. * * @param uid {String} user id * @param info {Object} record for logined user */ pro.addLoginedUser = function(uid, info) { if(!this.logined[uid]) { this.loginedCount++; } info.uid = uid; this.logined[uid] = info; }; /** * Update user info. * @param uid {String} user id * @param info {Object} info for update. */ pro.updateUserInfo = function(uid, info) { var user = this.logined[uid]; if (!user) { return; } for (var p in info) { if (info.hasOwnProperty(p) && typeof info[p] !== 'function') { user[p] = info[p]; } } }; /** * Increase connection count */ pro.increaseConnectionCount = function() { this.connCount++; }; /** * Remote logined user * * @param uid {String} user id */ pro.removeLoginedUser = function(uid) { if(!!this.logined[uid]) { this.loginedCount--; } delete this.logined[uid]; }; /** * Decrease connection count * * @param uid {String} uid */ pro.decreaseConnectionCount = function(uid) { if(this.connCount) { this.connCount--; } if(!!uid) { this.removeLoginedUser(uid); } }; /** * Get statistics info * * @return {Object} statistics info */ pro.getStatisticsInfo = function() { var list = []; for(var uid in this.logined) { list.push(this.logined[uid]); } return {serverId: this.serverId, totalConnCount: this.connCount, loginedCount: this.loginedCount, loginedList: list}; }; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* Filter service.
* Register and fire before and after filters.
*/
var Service = function() {
this.befores = []; // before filters
this.afters = []; // after filters
};
module.exports = Service;
Service.prototype.name = 'filter';
/**
* Add before filter into the filter chain.
*
* @param filter {Object|Function} filter instance or filter function.
*/
Service.prototype.before = function(filter){
this.befores.push(filter);
};
/**
* Add after filter into the filter chain.
*
* @param filter {Object|Function} filter instance or filter function.
*/
Service.prototype.after = function(filter){
this.afters.unshift(filter);
};
/**
* TODO: other insert method for filter? such as unshift
*/
/**
* Do the before filter.
* Fail over if any filter pass err parameter to the next function.
*
* @param msg {Object} clienet request msg
* @param session {Object} a session object for current request
* @param cb {Function} cb(err) callback function to invoke next chain node
*/
Service.prototype.beforeFilter = function(msg, session, cb) {
var index = 0, self = this;
var next = function(err, resp, opts) {
if(err || index >= self.befores.length) {
cb(err, resp, opts);
return;
}
var handler = self.befores[index++];
if(typeof handler === 'function') {
handler(msg, session, next);
} else if(typeof handler.before === 'function') {
handler.before(msg, session, next);
} else {
logger.error('meet invalid before filter, handler or handler.before should be function.');
next(new Error('invalid before filter.'));
}
}; //end of next
next();
};
/**
* Do after filter chain.
* Give server a chance to do clean up jobs after request responsed.
* After filter can not change the request flow before.
* After filter should call the next callback to let the request pass to next after filter.
*
* @param err {Object} error object
* @param session {Object} session object for current request
* @param {Object} resp response object send to client
* @param cb {Function} cb(err) callback function to invoke next chain node
*/
Service.prototype.afterFilter = function(err, msg, session, resp, cb) {
var index = 0, self = this;
function next(err) {
//if done
if(index >= self.afters.length) {
cb(err);
return;
}
var handler = self.afters[index++];
if(typeof handler === 'function') {
handler(err, msg, session, resp, next);
} else if(typeof handler.after === 'function') {
handler.after(err, msg, session, resp, next);
} else {
logger.error('meet invalid after filter, handler or handler.after should be function.');
next(new Error('invalid after filter.'));
}
} //end of next
next(err);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var fs = require('fs');
var utils = require('../../util/utils');
var Loader = require('pomelo-loader');
var pathUtil = require('../../util/pathUtil');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var forwardLogger = require('pomelo-logger').getLogger('forward-log', __filename);
/**
* Handler service.
* Dispatch request to the relactive handler.
*
* @param {Object} app current application context
*/
var Service = function(app, opts) {
this.app = app;
this.handlerMap = {};
if(!!opts.reloadHandlers) {
watchHandlers(app, this.handlerMap);
}
this.enableForwardLog = opts.enableForwardLog || false;
};
module.exports = Service;
Service.prototype.name = 'handler';
/**
* Handler the request.
*/
Service.prototype.handle = function(routeRecord, msg, session, cb) {
// the request should be processed by current server
var handler = this.getHandler(routeRecord);
if(!handler) {
logger.error('[handleManager]: fail to find handler for %j', msg.__route__);
utils.invokeCallback(cb, new Error('fail to find handler for ' + msg.__route__));
return;
}
var start = Date.now();
var self = this;
var callback = function(err, resp, opts) {
if(self.enableForwardLog) {
var log = {
route : msg.__route__,
args : msg,
time : utils.format(new Date(start)),
timeUsed : new Date() - start
};
forwardLogger.info(JSON.stringify(log));
}
// resp = getResp(arguments);
utils.invokeCallback(cb, err, resp, opts);
}
var method = routeRecord.method;
if(!Array.isArray(msg)) {
handler[method](msg, session, callback);
} else {
msg.push(session);
msg.push(callback);
handler[method].apply(handler, msg);
}
return;
};
/**
* Get handler instance by routeRecord.
*
* @param {Object} handlers handler map
* @param {Object} routeRecord route record parsed from route string
* @return {Object} handler instance if any matchs or null for match fail
*/
Service.prototype.getHandler = function(routeRecord) {
var serverType = routeRecord.serverType;
if(!this.handlerMap[serverType]) {
loadHandlers(this.app, serverType, this.handlerMap);
}
var handlers = this.handlerMap[serverType] || {};
var handler = handlers[routeRecord.handler];
if(!handler) {
logger.warn('could not find handler for routeRecord: %j', routeRecord);
return null;
}
if(typeof handler[routeRecord.method] !== 'function') {
logger.warn('could not find the method %s in handler: %s', routeRecord.method, routeRecord.handler);
return null;
}
return handler;
};
/**
* Load handlers from current application
*/
var loadHandlers = function(app, serverType, handlerMap) {
var p = pathUtil.getHandlerPath(app.getBase(), serverType);
if(p) {
handlerMap[serverType] = Loader.load(p, app);
}
};
var watchHandlers = function(app, handlerMap) {
var p = pathUtil.getHandlerPath(app.getBase(), app.serverType);
if (!!p){
fs.watch(p, function(event, name) {
if(event === 'change') {
handlerMap[app.serverType] = Loader.load(p, app);
}
});
}
};
var getResp = function(args) {
var len = args.length;
if(len == 1) {
return [];
}
if(len == 2) {
return [args[1]];
}
if(len == 3) {
return [args[1], args[2]];
}
if(len == 4) {
return [args[1], args[2], args[3]];
}
var r = new Array(len);
for (var i = 1; i < len; i++) {
r[i] = args[i];
}
return r;
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var EventEmitter = require('events').EventEmitter;
var util = require('util');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var utils = require('../../util/utils');
var FRONTEND_SESSION_FIELDS = ['id', 'frontendId', 'uid', '__sessionService__'];
var EXPORTED_SESSION_FIELDS = ['id', 'frontendId', 'uid', 'settings'];
var ST_INITED = 0;
var ST_CLOSED = 1;
/**
* Session service maintains the internal session for each client connection.
*
* Session service is created by session component and is only
* <b>available</b> in frontend servers. You can access the service by
* `app.get('sessionService')` or `app.sessionService` in frontend servers.
*
* @param {Object} opts constructor parameters
* @class
* @constructor
*/
var SessionService = function(opts) {
opts = opts || {};
this.singleSession = opts.singleSession;
this.sessions = {}; // sid -> session
this.uidMap = {}; // uid -> sessions
};
module.exports = SessionService;
/**
* Create and return internal session.
*
* @param {Integer} sid uniqe id for the internal session
* @param {String} frontendId frontend server in which the internal session is created
* @param {Object} socket the underlying socket would be held by the internal session
*
* @return {Session}
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.create = function(sid, frontendId, socket) {
var session = new Session(sid, frontendId, socket, this);
this.sessions[session.id] = session;
return session;
};
/**
* Bind the session with a user id.
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.bind = function(sid, uid, cb) {
var session = this.sessions[sid];
if(!session) {
process.nextTick(function() {
cb(new Error('session does not exist, sid: ' + sid));
});
return;
}
if(session.uid) {
if(session.uid === uid) {
// already bound with the same uid
cb();
return;
}
// already bound with other uid
process.nextTick(function() {
cb(new Error('session has already bound with ' + session.uid));
});
return;
}
var sessions = this.uidMap[uid];
if(!!this.singleSession && !!sessions) {
process.nextTick(function() {
cb(new Error('singleSession is enabled, and session has already bound with uid: ' + uid));
});
return;
}
if(!sessions) {
sessions = this.uidMap[uid] = [];
}
for(var i=0, l=sessions.length; i<l; i++) {
// session has binded with the uid
if(sessions[i].id === session.id) {
process.nextTick(cb);
return;
}
}
sessions.push(session);
session.bind(uid);
if(cb) {
process.nextTick(cb);
}
};
/**
* Unbind a session with the user id.
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.unbind = function(sid, uid, cb) {
var session = this.sessions[sid];
if(!session) {
process.nextTick(function() {
cb(new Error('session does not exist, sid: ' + sid));
});
return;
}
if(!session.uid || session.uid !== uid) {
process.nextTick(function() {
cb(new Error('session has not bind with ' + session.uid));
});
return;
}
var sessions = this.uidMap[uid], sess;
if(sessions) {
for(var i=0, l=sessions.length; i<l; i++) {
sess = sessions[i];
if(sess.id === sid) {
sessions.splice(i, 1);
break;
}
}
if(sessions.length === 0) {
delete this.uidMap[uid];
}
}
session.unbind(uid);
if(cb) {
process.nextTick(cb);
}
};
/**
* Get session by id.
*
* @param {Number} id The session id
* @return {Session}
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.get = function(sid) {
return this.sessions[sid];
};
/**
* Get sessions by userId.
*
* @param {Number} uid User id associated with the session
* @return {Array} list of session binded with the uid
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.getByUid = function(uid) {
return this.uidMap[uid];
};
/**
* Remove session by key.
*
* @param {Number} sid The session id
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.remove = function(sid) {
var session = this.sessions[sid];
if(session) {
var uid = session.uid;
delete this.sessions[session.id];
var sessions = this.uidMap[uid];
if(!sessions) {
return;
}
for(var i=0, l=sessions.length; i<l; i++) {
if(sessions[i].id === sid) {
sessions.splice(i, 1);
if(sessions.length === 0) {
delete this.uidMap[uid];
}
break;
}
}
}
};
/**
* Import the key/value into session.
*
* @api private
*/
SessionService.prototype.import = function(sid, key, value, cb) {
var session = this.sessions[sid];
if(!session) {
utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));
return;
}
session.set(key, value);
utils.invokeCallback(cb);
};
/**
* Import new value for the existed session.
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.importAll = function(sid, settings, cb) {
var session = this.sessions[sid];
if(!session) {
utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));
return;
}
for(var f in settings) {
session.set(f, settings[f]);
}
utils.invokeCallback(cb);
};
/**
* Kick all the session offline under the user id.
*
* @param {Number} uid user id asscociated with the session
* @param {Function} cb callback function
*
* @memberOf SessionService
*/
SessionService.prototype.kick = function(uid, reason, cb) {
// compatible for old kick(uid, cb);
if(typeof reason === 'function') {
cb = reason;
reason = 'kick';
}
var sessions = this.getByUid(uid);
if(sessions) {
// notify client
var sids = [];
var self = this;
sessions.forEach(function(session) {
sids.push(session.id);
});
sids.forEach(function(sid) {
self.sessions[sid].closed(reason);
});
process.nextTick(function() {
utils.invokeCallback(cb);
});
} else {
process.nextTick(function() {
utils.invokeCallback(cb);
});
}
};
/**
* Kick a user offline by session id.
*
* @param {Number} sid session id
* @param {Function} cb callback function
*
* @memberOf SessionService
*/
SessionService.prototype.kickBySessionId = function(sid, reason, cb) {
if(typeof reason === 'function') {
cb = reason;
reason = 'kick';
}
var session = this.get(sid);
if(session) {
// notify client
session.closed(reason);
process.nextTick(function() {
utils.invokeCallback(cb);
});
} else {
process.nextTick(function() {
utils.invokeCallback(cb);
});
}
};
/**
* Get client remote address by session id.
*
* @param {Number} sid session id
* @return {Object} remote address of client
*
* @memberOf SessionService
*/
SessionService.prototype.getClientAddressBySessionId = function(sid) {
var session = this.get(sid);
if(session) {
var socket = session.__socket__;
return socket.remoteAddress;
} else {
return null;
}
};
/**
* Send message to the client by session id.
*
* @param {String} sid session id
* @param {Object} msg message to send
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.sendMessage = function(sid, msg) {
var session = this.sessions[sid];
if(!session) {
logger.debug('Fail to send message for non-existing session, sid: ' + sid + ' msg: ' + msg);
return false;
}
return send(this, session, msg);
};
/**
* Send message to the client by user id.
*
* @param {String} uid userId
* @param {Object} msg message to send
*
* @memberOf SessionService
* @api private
*/
SessionService.prototype.sendMessageByUid = function(uid, msg) {
var sessions = this.uidMap[uid];
if(!sessions) {
logger.debug('fail to send message by uid for non-existing session. uid: %j',
uid);
return false;
}
for(var i=0, l=sessions.length; i<l; i++) {
send(this, sessions[i], msg);
}
};
/**
* Iterate all the session in the session service.
*
* @param {Function} cb callback function to fetch session
* @api private
*/
SessionService.prototype.forEachSession = function(cb) {
for(var sid in this.sessions) {
cb(this.sessions[sid]);
}
};
/**
* Iterate all the binded session in the session service.
*
* @param {Function} cb callback function to fetch session
* @api private
*/
SessionService.prototype.forEachBindedSession = function(cb) {
var i, l, sessions;
for(var uid in this.uidMap) {
sessions = this.uidMap[uid];
for(i=0, l=sessions.length; i<l; i++) {
cb(sessions[i]);
}
}
};
/**
* Get sessions' quantity in specified server.
*
*/
SessionService.prototype.getSessionsCount = function() {
return utils.size(this.sessions);
};
/**
* Send message to the client that associated with the session.
*
* @api private
*/
var send = function(service, session, msg) {
session.send(msg);
return true;
};
/**
* Session maintains the relationship between client connection and user information.
* There is a session associated with each client connection. And it should bind to a
* user id after the client passes the identification.
*
* Session is created in frontend server and should not be accessed in handler.
* There is a proxy class called BackendSession in backend servers and FrontendSession
* in frontend servers.
*/
var Session = function(sid, frontendId, socket, service) {
EventEmitter.call(this);
this.id = sid; // r
this.frontendId = frontendId; // r
this.uid = null; // r
this.settings = {};
// private
this.__socket__ = socket;
this.__sessionService__ = service;
this.__state__ = ST_INITED;
};
util.inherits(Session, EventEmitter);
/*
* Export current session as frontend session.
*/
Session.prototype.toFrontendSession = function() {
return new FrontendSession(this);
};
/**
* Bind the session with the the uid.
*
* @param {Number} uid User id
* @api public
*/
Session.prototype.bind = function(uid) {
this.uid = uid;
this.emit('bind', uid);
};
/**
* Unbind the session with the the uid.
*
* @param {Number} uid User id
* @api private
*/
Session.prototype.unbind = function(uid) {
this.uid = null;
this.emit('unbind', uid);
};
/**
* Set values (one or many) for the session.
*
* @param {String|Object} key session key
* @param {Object} value session value
* @api public
*/
Session.prototype.set = function(key, value) {
if (utils.isObject(key)) {
for (var i in key) {
this.settings[i] = key[i];
}
} else {
this.settings[key] = value;
}
};
/**
* Remove value from the session.
*
* @param {String} key session key
* @api public
*/
Session.prototype.remove = function(key) {
delete this[key];
};
/**
* Get value from the session.
*
* @param {String} key session key
* @return {Object} value associated with session key
* @api public
*/
Session.prototype.get = function(key) {
return this.settings[key];
};
/**
* Send message to the session.
*
* @param {Object} msg final message sent to client
*/
Session.prototype.send = function(msg) {
this.__socket__.send(msg);
};
/**
* Send message to the session in batch.
*
* @param {Array} msgs list of message
*/
Session.prototype.sendBatch = function(msgs) {
this.__socket__.sendBatch(msgs);
};
/**
* Closed callback for the session which would disconnect client in next tick.
*
* @api public
*/
Session.prototype.closed = function(reason) {
logger.debug('session on [%s] is closed with session id: %s', this.frontendId, this.id);
if(this.__state__ === ST_CLOSED) {
return;
}
this.__state__ = ST_CLOSED;
this.__sessionService__.remove(this.id);
this.emit('closed', this.toFrontendSession(), reason);
this.__socket__.emit('closing', reason);
var self = this;
// give a chance to send disconnect message to client
process.nextTick(function() {
self.__socket__.disconnect();
});
};
/**
* Frontend session for frontend server.
*/
var FrontendSession = function(session) {
EventEmitter.call(this);
clone(session, this, FRONTEND_SESSION_FIELDS);
// deep copy for settings
this.settings = dclone(session.settings);
this.__session__ = session;
};
util.inherits(FrontendSession, EventEmitter);
FrontendSession.prototype.bind = function(uid, cb) {
var self = this;
this.__sessionService__.bind(this.id, uid, function(err) {
if(!err) {
self.uid = uid;
}
utils.invokeCallback(cb, err);
});
};
FrontendSession.prototype.unbind = function(uid, cb) {
var self = this;
this.__sessionService__.unbind(this.id, uid, function(err) {
if(!err) {
self.uid = null;
}
utils.invokeCallback(cb, err);
});
};
FrontendSession.prototype.set = function(key, value) {
this.settings[key] = value;
};
FrontendSession.prototype.get = function(key) {
return this.settings[key];
};
FrontendSession.prototype.push = function(key, cb) {
this.__sessionService__.import(this.id, key, this.get(key), cb);
};
FrontendSession.prototype.pushAll = function(cb) {
this.__sessionService__.importAll(this.id, this.settings, cb);
};
FrontendSession.prototype.on = function(event, listener) {
EventEmitter.prototype.on.call(this, event, listener);
this.__session__.on(event, listener);
};
/**
* Export the key/values for serialization.
*
* @api private
*/
FrontendSession.prototype.export = function() {
var res = {};
clone(this, res, EXPORTED_SESSION_FIELDS);
return res;
};
var clone = function(src, dest, includes) {
var f;
for(var i=0, l=includes.length; i<l; i++) {
f = includes[i];
dest[f] = src[f];
}
};
var dclone = function(src) {
var res = {};
for(var f in src) {
res[f] = src[f];
}
return res;
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| backendSession.js | 28.57% | (2 / 7) | 100% | (0 / 0) | 0% | (0 / 1) | 28.57% | (2 / 7) | |
| channel.js | 33.33% | (2 / 6) | 100% | (0 / 0) | 0% | (0 / 1) | 33.33% | (2 / 6) | |
| connection.js | 23.53% | (4 / 17) | 0% | (0 / 6) | 0% | (0 / 5) | 23.53% | (4 / 17) | |
| connector.js | 10.94% | (29 / 265) | 0% | (0 / 134) | 0% | (0 / 37) | 11.03% | (29 / 263) | |
| dictionary.js | 24.56% | (14 / 57) | 0% | (0 / 12) | 0% | (0 / 6) | 24.56% | (14 / 57) | |
| master.js | 63.64% | (7 / 11) | 100% | (0 / 0) | 0% | (0 / 4) | 63.64% | (7 / 11) | |
| monitor.js | 61.54% | (8 / 13) | 100% | (0 / 0) | 0% | (0 / 5) | 61.54% | (8 / 13) | |
| protobuf.js | 25% | (18 / 72) | 0% | (0 / 22) | 0% | (0 / 11) | 25% | (18 / 72) | |
| proxy.js | 21.3% | (23 / 108) | 0% | (0 / 51) | 0% | (0 / 17) | 21.3% | (23 / 108) | |
| pushScheduler.js | 16.36% | (9 / 55) | 0% | (0 / 32) | 0% | (0 / 8) | 16.36% | (9 / 55) | |
| remote.js | 25.58% | (11 / 43) | 0% | (0 / 17) | 0% | (0 / 6) | 25.58% | (11 / 43) | |
| server.js | 50% | (10 / 20) | 100% | (0 / 0) | 0% | (0 / 7) | 50% | (10 / 20) | |
| session.js | 20% | (4 / 20) | 0% | (0 / 8) | 0% | (0 / 5) | 20% | (4 / 20) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 1 1 | var BackendSessionService = require('../common/service/backendSessionService');
module.exports = function(app) {
var service = new BackendSessionService(app);
service.name = '__backendSession__';
// export backend session service to the application context.
app.set('backendSessionService', service, true);
// for compatibility as `LocalSession` is renamed to `BackendSession`
app.set('localSessionService', service, true);
return service;
};
|
| 1 2 3 4 5 6 7 8 9 | 1 1 | var ChannelService = require('../common/service/channelService');
module.exports = function(app, opts) {
var service = new ChannelService(app, opts);
app.set('channelService', service, true);
service.name = '__channel__';
return service;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 1 1 1 1 | var ConnectionService = require('../common/service/connectionService');
/**
* Connection component for statistics connection status of frontend servers
*/
module.exports = function(app) {
return new Component(app);
};
var Component = function(app) {
this.app = app;
this.service = new ConnectionService(app);
// proxy the service methods except the lifecycle interfaces of component
var method, self = this;
var getFun = function(m) {
return (function() {
return function() {
return self.service[m].apply(self.service, arguments);
};
})();
};
for(var m in this.service) {
if(m !== 'start' && m !== 'stop') {
method = this.service[m];
if(typeof method === 'function') {
this[m] = getFun(m);
}
}
}
};
Component.prototype.name = '__connection__';
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var taskManager = require('../common/manager/taskManager');
var pomelo = require('../pomelo');
var rsa = require("node-bignumber");
var events = require('../util/events');
var utils = require('../util/utils');
module.exports = function(app, opts) {
return new Component(app, opts);
};
/**
* Connector component. Receive client requests and attach session with socket.
*
* @param {Object} app current application context
* @param {Object} opts attach parameters
* opts.connector {Object} provides low level network and protocol details implementation between server and clients.
*/
var Component = function(app, opts) {
opts = opts || {};
this.app = app;
this.connector = getConnector(app, opts);
this.encode = opts.encode;
this.decode = opts.decode;
this.useCrypto = opts.useCrypto;
this.useHostFilter = opts.useHostFilter;
this.useAsyncCoder = opts.useAsyncCoder;
this.blacklistFun = opts.blacklistFun;
this.keys = {};
this.blacklist = [];
if (opts.useDict) {
app.load(pomelo.dictionary, app.get('dictionaryConfig'));
}
if (opts.useProtobuf) {
app.load(pomelo.protobuf, app.get('protobufConfig'));
}
// component dependencies
this.server = null;
this.session = null;
this.connection = null;
};
var pro = Component.prototype;
pro.name = '__connector__';
pro.start = function(cb) {
this.server = this.app.components.__server__;
this.session = this.app.components.__session__;
this.connection = this.app.components.__connection__;
// check component dependencies
if (!this.server) {
process.nextTick(function() {
utils.invokeCallback(cb, new Error('fail to start connector component for no server component loaded'));
});
return;
}
if (!this.session) {
process.nextTick(function() {
utils.invokeCallback(cb, new Error('fail to start connector component for no session component loaded'));
});
return;
}
process.nextTick(cb);
};
pro.afterStart = function(cb) {
this.connector.start(cb);
this.connector.on('connection', hostFilter.bind(this, bindEvents));
};
pro.stop = function(force, cb) {
if (this.connector) {
this.connector.stop(force, cb);
this.connector = null;
return;
} else {
process.nextTick(cb);
}
};
pro.send = function(reqId, route, msg, recvs, opts, cb) {
logger.debug('[%s] send message reqId: %s, route: %s, msg: %j, receivers: %j, opts: %j', this.app.serverId, reqId, route, msg, recvs, opts);
if (this.useAsyncCoder) {
return this.sendAsync(reqId, route, msg, recvs, opts, cb);
}
var emsg = msg;
if (this.encode) {
// use costumized encode
emsg = this.encode.call(this, reqId, route, msg);
} else if (this.connector.encode) {
// use connector default encode
emsg = this.connector.encode(reqId, route, msg);
}
this.doSend(reqId, route, emsg, recvs, opts, cb);
};
pro.sendAsync = function(reqId, route, msg, recvs, opts, cb) {
var emsg = msg;
var self = this;
if (this.encode) {
// use costumized encode
this.encode(reqId, route, msg, function(err, encodeMsg) {
if (err) {
return cb(err);
}
emsg = encodeMsg;
self.doSend(reqId, route, emsg, recvs, opts, cb);
});
} else if (this.connector.encode) {
// use connector default encode
this.connector.encode(reqId, route, msg, function(err, encodeMsg) {
if (err) {
return cb(err);
}
emsg = encodeMsg;
self.doSend(reqId, route, emsg, recvs, opts, cb);
});
}
}
pro.doSend = function(reqId, route, emsg, recvs, opts, cb) {
if (!emsg) {
process.nextTick(function() {
return cb && cb(new Error('fail to send message for encode result is empty.'));
});
}
this.app.components.__pushScheduler__.schedule(reqId, route, emsg,
recvs, opts, cb);
}
pro.setPubKey = function(id, key) {
var pubKey = new rsa.Key();
pubKey.n = new rsa.BigInteger(key.rsa_n, 16);
pubKey.e = key.rsa_e;
this.keys[id] = pubKey;
};
pro.getPubKey = function(id) {
return this.keys[id];
};
var getConnector = function(app, opts) {
var connector = opts.connector;
if (!connector) {
return getDefaultConnector(app, opts);
}
if (typeof connector !== 'function') {
return connector;
}
var curServer = app.getCurServer();
return connector(curServer.clientPort, curServer.host, opts);
};
var getDefaultConnector = function(app, opts) {
var DefaultConnector = require('../connectors/sioconnector');
var curServer = app.getCurServer();
return new DefaultConnector(curServer.clientPort, curServer.host, opts);
};
var hostFilter = function(cb, socket) {
if(!this.useHostFilter) {
return cb(this, socket);
}
var ip = socket.remoteAddress.ip;
var check = function(list) {
for (var address in list) {
var exp = new RegExp(list[address]);
if (exp.test(ip)) {
socket.disconnect();
return true;
}
}
return false;
};
// dynamical check
if (this.blacklist.length !== 0 && !!check(this.blacklist)) {
return;
}
// static check
if (!!this.blacklistFun && typeof this.blacklistFun === 'function') {
var self = this;
self.blacklistFun(function(err, list) {
if (!!err) {
logger.error('connector blacklist error: %j', err.stack);
utils.invokeCallback(cb, self, socket);
return;
}
if (!Array.isArray(list)) {
logger.error('connector blacklist is not array: %j', list);
utils.invokeCallback(cb, self, socket);
return;
}
if (!!check(list)) {
return;
} else {
utils.invokeCallback(cb, self, socket);
return;
}
});
} else {
utils.invokeCallback(cb, this, socket);
}
};
var bindEvents = function(self, socket) {
var curServer = self.app.getCurServer();
var maxConnections = curServer['max-connections'];
if (self.connection && maxConnections) {
self.connection.increaseConnectionCount();
var statisticInfo = self.connection.getStatisticsInfo();
if (statisticInfo.totalConnCount > maxConnections) {
logger.warn('the server %s has reached the max connections %s', curServer.id, maxConnections);
socket.disconnect();
return;
}
}
//create session for connection
var session = getSession(self, socket);
var closed = false;
socket.on('disconnect', function() {
if (closed) {
return;
}
closed = true;
if (self.connection) {
self.connection.decreaseConnectionCount(session.uid);
}
});
socket.on('error', function() {
if (closed) {
return;
}
closed = true;
if (self.connection) {
self.connection.decreaseConnectionCount(session.uid);
}
});
// new message
socket.on('message', function(msg) {
var dmsg = msg;
if (self.useAsyncCoder) {
return handleMessageAsync(self, msg, session, socket);
}
if (self.decode) {
dmsg = self.decode(msg, session);
} else if (self.connector.decode) {
dmsg = self.connector.decode(msg, socket);
}
if (!dmsg) {
// discard invalid message
return;
}
// use rsa crypto
if (self.useCrypto) {
var verified = verifyMessage(self, session, dmsg);
if (!verified) {
logger.error('fail to verify the data received from client.');
return;
}
}
handleMessage(self, session, dmsg);
}); //on message end
};
var handleMessageAsync = function(self, msg, session, socket) {
if (self.decode) {
self.decode(msg, session, function(err, dmsg) {
if (err) {
logger.error('fail to decode message from client %s .', err.stack);
return;
}
doHandleMessage(self, dmsg, session);
});
} else if (self.connector.decode) {
self.connector.decode(msg, socket, function(err, dmsg) {
if (err) {
logger.error('fail to decode message from client %s .', err.stack);
return;
}
doHandleMessage(self, dmsg, session);
});
}
}
var doHandleMessage = function(self, dmsg, session) {
if (!dmsg) {
// discard invalid message
return;
}
// use rsa crypto
if (self.useCrypto) {
var verified = verifyMessage(self, session, dmsg);
if (!verified) {
logger.error('fail to verify the data received from client.');
return;
}
}
handleMessage(self, session, dmsg);
}
/**
* get session for current connection
*/
var getSession = function(self, socket) {
var app = self.app,
sid = socket.id;
var session = self.session.get(sid);
if (session) {
return session;
}
session = self.session.create(sid, app.getServerId(), socket);
logger.debug('[%s] getSession session is created with session id: %s', app.getServerId(), sid);
// bind events for session
socket.on('disconnect', session.closed.bind(session));
socket.on('error', session.closed.bind(session));
session.on('closed', onSessionClose.bind(null, app));
session.on('bind', function(uid) {
logger.debug('session on [%s] bind with uid: %s', self.app.serverId, uid);
// update connection statistics if necessary
if (self.connection) {
self.connection.addLoginedUser(uid, {
loginTime: Date.now(),
uid: uid,
address: socket.remoteAddress.ip + ':' + socket.remoteAddress.port
});
}
self.app.event.emit(events.BIND_SESSION, session);
});
session.on('unbind', function(uid) {
if (self.connection) {
self.connection.removeLoginedUser(uid);
}
self.app.event.emit(events.UNBIND_SESSION, session);
});
return session;
};
var onSessionClose = function(app, session, reason) {
taskManager.closeQueue(session.id, true);
app.event.emit(events.CLOSE_SESSION, session);
};
var handleMessage = function(self, session, msg) {
logger.debug('[%s] handleMessage session id: %s, msg: %j', self.app.serverId, session.id, msg);
var type = checkServerType(msg.route);
if (!type) {
logger.error('invalid route string. route : %j', msg.route);
return;
}
self.server.globalHandle(msg, session.toFrontendSession(), function(err, resp, opts) {
if (resp && !msg.id) {
logger.warn('try to response to a notify: %j', msg.route);
return;
}
if (!msg.id && !resp) return;
if (!resp) resp = {};
if (!!err && !resp.code) {
resp.code = 500;
}
opts = {
type: 'response',
userOptions: opts || {}
};
// for compatiablity
opts.isResponse = true;
self.send(msg.id, msg.route, resp, [session.id], opts,
function() {});
});
};
/**
* Get server type form request message.
*/
var checkServerType = function(route) {
if (!route) {
return null;
}
var idx = route.indexOf('.');
if (idx < 0) {
return null;
}
return route.substring(0, idx);
};
var verifyMessage = function(self, session, msg) {
var sig = msg.body.__crypto__;
if (!sig) {
logger.error('receive data from client has no signature [%s]', self.app.serverId);
return false;
}
var pubKey;
if (!session) {
logger.error('could not find session.');
return false;
}
if (!session.get('pubKey')) {
pubKey = self.getPubKey(session.id);
if (!!pubKey) {
delete self.keys[session.id];
session.set('pubKey', pubKey);
} else {
logger.error('could not get public key, session id is %s', session.id);
return false;
}
} else {
pubKey = session.get('pubKey');
}
if (!pubKey.n || !pubKey.e) {
logger.error('could not verify message without public key [%s]', self.app.serverId);
return false;
}
delete msg.body.__crypto__;
var message = JSON.stringify(msg.body);
if (utils.hasChineseChar(message))
message = utils.unicodeToUtf8(message);
return pubKey.verifyString(message, sig);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var fs = require('fs');
var path = require('path');
var utils = require('../util/utils');
var Loader = require('pomelo-loader');
var pathUtil = require('../util/pathUtil');
var crypto = require('crypto');
module.exports = function(app, opts) {
return new Component(app, opts);
};
var Component = function(app, opts) {
this.app = app;
this.dict = {};
this.abbrs = {};
this.userDicPath = null;
this.version = "";
//Set user dictionary
var p = path.join(app.getBase(), '/config/dictionary.json');
if(!!opts && !!opts.dict) {
p = opts.dict;
}
if(fs.existsSync(p)) {
this.userDicPath = p;
}
};
var pro = Component.prototype;
pro.name = '__dictionary__';
pro.start = function(cb) {
var servers = this.app.get('servers');
var routes = [];
//Load all the handler files
for(var serverType in servers) {
var p = pathUtil.getHandlerPath(this.app.getBase(), serverType);
if(!p) {
continue;
}
var handlers = Loader.load(p, this.app);
for(var name in handlers) {
var handler = handlers[name];
for(var key in handler) {
if(typeof(handler[key]) === 'function') {
routes.push(serverType + '.' + name + '.' + key);
}
}
}
}
//Sort the route to make sure all the routers abbr are the same in all the servers
routes.sort();
var abbr;
var i;
for(i = 0; i < routes.length; i++) {
abbr = i + 1;
this.abbrs[abbr] = routes[i];
this.dict[routes[i]] = abbr;
}
//Load user dictionary
if(!!this.userDicPath) {
var userDic = require(this.userDicPath);
abbr = routes.length + 1;
for(i = 0; i < userDic.length; i++) {
var route = userDic[i];
this.abbrs[abbr] = route;
this.dict[route] = abbr;
abbr++;
}
}
this.version = crypto.createHash('md5').update(JSON.stringify(this.dict)).digest('base64');
utils.invokeCallback(cb);
};
pro.getDict = function() {
return this.dict;
};
pro.getAbbrs = function() {
return this.abbrs;
};
pro.getVersion = function() {
return this.version;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 1 1 1 1 1 1 1 | /**
* Component for master.
*/
var Master = require('../master/master');
/**
* Component factory function
*
* @param {Object} app current application context
* @return {Object} component instances
*/
module.exports = function (app, opts) {
return new Component(app, opts);
};
/**
* Master component class
*
* @param {Object} app current application context
*/
var Component = function (app, opts) {
this.master = new Master(app, opts);
};
var pro = Component.prototype;
pro.name = '__master__';
/**
* Component lifecycle function
*
* @param {Function} cb
* @return {Void}
*/
pro.start = function (cb) {
this.master.start(cb);
};
/**
* Component lifecycle function
*
* @param {Boolean} force whether stop the component immediately
* @param {Function} cb
* @return {Void}
*/
pro.stop = function (force, cb) {
this.master.stop(cb);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 1 1 1 1 1 1 1 1 | /**
* Component for monitor.
* Load and start monitor client.
*/
var Monitor = require('../monitor/monitor');
/**
* Component factory function
*
* @param {Object} app current application context
* @return {Object} component instances
*/
module.exports = function(app, opts) {
return new Component(app, opts);
};
var Component = function(app, opts) {
this.monitor = new Monitor(app, opts);
};
var pro = Component.prototype;
pro.name = '__monitor__';
pro.start = function(cb) {
this.monitor.start(cb);
};
pro.stop = function(force, cb) {
this.monitor.stop(cb);
};
pro.reconnect = function(masterInfo) {
this.monitor.reconnect(masterInfo);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var fs = require('fs');
var path = require('path');
var protobuf = require('pomelo-protobuf');
var Constants = require('../util/constants');
var crypto = require('crypto');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
module.exports = function(app, opts) {
return new Component(app, opts);
};
var Component = function(app, opts) {
this.app = app;
opts = opts || {};
this.watchers = {};
this.serverProtos = {};
this.clientProtos = {};
this.version = "";
var env = app.get(Constants.RESERVED.ENV);
var originServerPath = path.join(app.getBase(), Constants.FILEPATH.SERVER_PROTOS);
var presentServerPath = path.join(Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.SERVER_PROTOS));
var originClientPath = path.join(app.getBase(), Constants.FILEPATH.CLIENT_PROTOS);
var presentClientPath = path.join(Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.CLIENT_PROTOS));
this.serverProtosPath = opts.serverProtos || (fs.existsSync(originServerPath) ? Constants.FILEPATH.SERVER_PROTOS : presentServerPath);
this.clientProtosPath = opts.clientProtos || (fs.existsSync(originClientPath) ? Constants.FILEPATH.CLIENT_PROTOS : presentClientPath);
this.setProtos(Constants.RESERVED.SERVER, path.join(app.getBase(), this.serverProtosPath));
this.setProtos(Constants.RESERVED.CLIENT, path.join(app.getBase(), this.clientProtosPath));
protobuf.init({encoderProtos:this.serverProtos, decoderProtos:this.clientProtos});
};
var pro = Component.prototype;
pro.name = '__protobuf__';
pro.encode = function(key, msg) {
return protobuf.encode(key, msg);
};
pro.encode2Bytes = function(key, msg) {
return protobuf.encode2Bytes(key, msg);
};
pro.decode = function(key, msg) {
return protobuf.decode(key, msg);
};
pro.getProtos = function() {
return {
server : this.serverProtos,
client : this.clientProtos,
version : this.version
};
};
pro.getVersion = function() {
return this.version;
};
pro.setProtos = function(type, path) {
if(!fs.existsSync(path)) {
return;
}
if(type === Constants.RESERVED.SERVER) {
this.serverProtos = protobuf.parse(require(path));
}
if(type === Constants.RESERVED.CLIENT) {
this.clientProtos = protobuf.parse(require(path));
}
var protoStr = JSON.stringify(this.clientProtos) + JSON.stringify(this.serverProtos);
this.version = crypto.createHash('md5').update(protoStr).digest('base64');
//Watch file
var watcher = fs.watch(path, this.onUpdate.bind(this, type, path));
if (this.watchers[type]) {
this.watchers[type].close();
}
this.watchers[type] = watcher;
};
pro.onUpdate = function(type, path, event) {
if(event !== 'change') {
return;
}
var self = this;
fs.readFile(path, 'utf8' ,function(err, data) {
try {
var protos = protobuf.parse(JSON.parse(data));
if(type === Constants.RESERVED.SERVER) {
protobuf.setEncoderProtos(protos);
self.serverProtos = protos;
} else {
protobuf.setDecoderProtos(protos);
self.clientProtos = protos;
}
var protoStr = JSON.stringify(self.clientProtos) + JSON.stringify(self.serverProtos);
self.version = crypto.createHash('md5').update(protoStr).digest('base64');
logger.info('change proto file , type : %j, path : %j, version : %j', type, path, self.version);
} catch(e) {
logger.warn("change proto file error! path : %j", path);
logger.warn(e);
}
});
};
pro.stop = function(force, cb) {
for (var type in this.watchers) {
this.watchers[type].close();
}
this.watchers = {};
process.nextTick(cb);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* Component for proxy.
* Generate proxies for rpc client.
*/
var crc = require('crc');
var utils = require('../util/utils');
var events = require('../util/events');
var Client = require('pomelo-rpc').client;
var pathUtil = require('../util/pathUtil');
var Constants = require('../util/constants');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* Component factory function
*
* @param {Object} app current application context
* @param {Object} opts construct parameters
* opts.router: (optional) rpc message route function, route(routeParam, msg, cb),
* opts.mailBoxFactory: (optional) mail box factory instance.
* @return {Object} component instance
*/
module.exports = function(app, opts) {
opts = opts || {};
// proxy default config
// cacheMsg is deprecated, just for compatibility here.
opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;
opts.interval = opts.interval || 30;
opts.router = genRouteFun();
opts.context = app;
opts.routeContext = app;
if (app.enabled('rpcDebugLog')) {
opts.rpcDebugLog = true;
opts.rpcLogger = require('pomelo-logger').getLogger('rpc-debug', __filename);
}
return new Component(app, opts);
};
/**
* Proxy component class
*
* @param {Object} app current application context
* @param {Object} opts construct parameters
*/
var Component = function(app, opts) {
this.app = app;
this.opts = opts;
this.client = genRpcClient(this.app, opts);
this.app.event.on(events.ADD_SERVERS, this.addServers.bind(this));
this.app.event.on(events.REMOVE_SERVERS, this.removeServers.bind(this));
this.app.event.on(events.REPLACE_SERVERS, this.replaceServers.bind(this));
};
var pro = Component.prototype;
pro.name = '__proxy__';
/**
* Proxy component lifecycle function
*
* @param {Function} cb
* @return {Void}
*/
pro.start = function(cb) {
if(this.opts.enableRpcLog) {
logger.warn('enableRpcLog is deprecated in 0.8.0, please use app.rpcFilter(pomelo.rpcFilters.rpcLog())');
}
var rpcBefores = this.app.get(Constants.KEYWORDS.RPC_BEFORE_FILTER);
var rpcAfters = this.app.get(Constants.KEYWORDS.RPC_AFTER_FILTER);
var rpcErrorHandler = this.app.get(Constants.RESERVED.RPC_ERROR_HANDLER);
if(!!rpcBefores) {
this.client.before(rpcBefores);
}
if(!!rpcAfters) {
this.client.after(rpcAfters);
}
if(!!rpcErrorHandler) {
this.client.setErrorHandler(rpcErrorHandler);
}
process.nextTick(cb);
};
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
pro.afterStart = function(cb) {
var self = this;
this.app.__defineGetter__('rpc', function() {
return self.client.proxies.user;
});
this.app.__defineGetter__('sysrpc', function() {
return self.client.proxies.sys;
});
this.app.set('rpcInvoke', this.client.rpcInvoke.bind(this.client), true);
this.client.start(cb);
};
/**
* Add remote server to the rpc client.
*
* @param {Array} servers server info list, {id, serverType, host, port}
*/
pro.addServers = function(servers) {
if (!servers || !servers.length) {
return;
}
genProxies(this.client, this.app, servers);
this.client.addServers(servers);
};
/**
* Remove remote server from the rpc client.
*
* @param {Array} ids server id list
*/
pro.removeServers = function(ids) {
this.client.removeServers(ids);
};
/**
* Replace remote servers from the rpc client.
*
* @param {Array} ids server id list
*/
pro.replaceServers = function(servers) {
if (!servers || !servers.length) {
return;
}
// update proxies
this.client.proxies = {};
genProxies(this.client, this.app, servers);
this.client.replaceServers(servers);
};
/**
* Proxy for rpc client rpcInvoke.
*
* @param {String} serverId remote server id
* @param {Object} msg rpc message: {serverType: serverType, service: serviceName, method: methodName, args: arguments}
* @param {Function} cb callback function
*/
pro.rpcInvoke = function(serverId, msg, cb) {
this.client.rpcInvoke(serverId, msg, cb);
};
/**
* Generate rpc client
*
* @param {Object} app current application context
* @param {Object} opts contructor parameters for rpc client
* @return {Object} rpc client
*/
var genRpcClient = function(app, opts) {
opts.context = app;
opts.routeContext = app;
if(!!opts.rpcClient) {
return opts.rpcClient.create(opts);
} else {
return Client.create(opts);
}
};
/**
* Generate proxy for the server infos.
*
* @param {Object} client rpc client instance
* @param {Object} app application context
* @param {Array} sinfos server info list
*/
var genProxies = function(client, app, sinfos) {
var item;
for (var i = 0, l = sinfos.length; i < l; i++) {
item = sinfos[i];
if (hasProxy(client, item)) {
continue;
}
client.addProxies(getProxyRecords(app, item));
}
};
/**
* Check a server whether has generated proxy before
*
* @param {Object} client rpc client instance
* @param {Object} sinfo server info
* @return {Boolean} true or false
*/
var hasProxy = function(client, sinfo) {
var proxy = client.proxies;
return !!proxy.sys && !! proxy.sys[sinfo.serverType];
};
/**
* Get proxy path for rpc client.
* Iterate all the remote service path and create remote path record.
*
* @param {Object} app current application context
* @param {Object} sinfo server info, format: {id, serverType, host, port}
* @return {Array} remote path record array
*/
var getProxyRecords = function(app, sinfo) {
var records = [],
appBase = app.getBase(),
record;
// sys remote service path record
if (app.isFrontend(sinfo)) {
record = pathUtil.getSysRemotePath('frontend');
} else {
record = pathUtil.getSysRemotePath('backend');
}
if (record) {
records.push(pathUtil.remotePathRecord('sys', sinfo.serverType, record));
}
// user remote service path record
record = pathUtil.getUserRemotePath(appBase, sinfo.serverType);
if (record) {
records.push(pathUtil.remotePathRecord('user', sinfo.serverType, record));
}
return records;
};
var genRouteFun = function() {
return function(session, msg, app, cb) {
var routes = app.get('__routes__');
if (!routes) {
defaultRoute(session, msg, app, cb);
return;
}
var type = msg.serverType,
route = routes[type] || routes['default'];
if (route) {
route(session, msg, app, cb);
} else {
defaultRoute(session, msg, app, cb);
}
};
};
var defaultRoute = function(session, msg, app, cb) {
var list = app.getServersByType(msg.serverType);
if (!list || !list.length) {
cb(new Error('can not find server info for type:' + msg.serverType));
return;
}
var uid = session ? (session.uid || '') : '';
var index = Math.abs(crc.crc32(uid.toString())) % list.length;
utils.invokeCallback(cb, null, list[index].id);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 1 1 1 1 1 1 1 1 1 | /**
* Scheduler component to schedule message sending.
*/
var DefaultScheduler = require('../pushSchedulers/direct');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
module.exports = function(app, opts) {
return new PushScheduler(app, opts);
};
var PushScheduler = function(app, opts) {
this.app = app;
opts = opts || {};
this.scheduler = getScheduler(this, app, opts);
};
PushScheduler.prototype.name = '__pushScheduler__';
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
PushScheduler.prototype.afterStart = function(cb) {
if(this.isSelectable) {
for (var k in this.scheduler) {
var sch = this.scheduler[k];
if(typeof sch.start === 'function') {
sch.start();
}
}
process.nextTick(cb);
} else if(typeof this.scheduler.start === 'function') {
this.scheduler.start(cb);
} else {
process.nextTick(cb);
}
};
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
PushScheduler.prototype.stop = function(force, cb) {
if(this.isSelectable) {
for (var k in this.scheduler) {
var sch = this.scheduler[k];
if(typeof sch.stop === 'function') {
sch.stop();
}
}
process.nextTick(cb);
} else if(typeof this.scheduler.stop === 'function') {
this.scheduler.stop(cb);
} else {
process.nextTick(cb);
}
};
/**
* Schedule how the message to send.
*
* @param {Number} reqId request id
* @param {String} route route string of the message
* @param {Object} msg message content after encoded
* @param {Array} recvs array of receiver's session id
* @param {Object} opts options
* @param {Function} cb
*/
PushScheduler.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {
var self = this;
if(self.isSelectable) {
if(typeof self.selector === 'function') {
self.selector(reqId, route, msg, recvs, opts, function(id) {
if(self.scheduler[id] && typeof self.scheduler[id].schedule === 'function') {
self.scheduler[id].schedule(reqId, route, msg, recvs, opts, cb);
} else {
logger.error('invalid pushScheduler id, id: %j', id);
}
});
} else {
logger.error('the selector for pushScheduler is not a function, selector: %j', self.selector);
}
} else {
if (typeof self.scheduler.schedule === 'function') {
self.scheduler.schedule(reqId, route, msg, recvs, opts, cb);
} else {
logger.error('the scheduler does not have a schedule function, scheduler: %j', self.scheduler);
}
}
};
var getScheduler = function(pushSchedulerComp, app, opts) {
var scheduler = opts.scheduler || DefaultScheduler;
if(typeof scheduler === 'function') {
return scheduler(app, opts);
}
if(Array.isArray(scheduler)) {
var res = {};
scheduler.forEach(function(sch) {
if(typeof sch.scheduler === 'function') {
res[sch.id] = sch.scheduler(app, sch.options);
} else {
res[sch.id] = sch.scheduler;
}
});
pushSchedulerComp.isSelectable = true;
pushSchedulerComp.selector = opts.selector;
return res;
}
return scheduler;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 1 1 1 1 1 1 1 1 1 1 1 | /**
* Component for remote service.
* Load remote service and add to global context.
*/
var fs = require('fs');
var pathUtil = require('../util/pathUtil');
var RemoteServer = require('pomelo-rpc').server;
/**
* Remote component factory function
*
* @param {Object} app current application context
* @param {Object} opts construct parameters
* opts.acceptorFactory {Object}: acceptorFactory.create(opts, cb)
* @return {Object} remote component instances
*/
module.exports = function(app, opts) {
opts = opts || {};
// cacheMsg is deprecated, just for compatibility here.
opts.bufferMsg = opts.bufferMsg || opts.cacheMsg || false;
opts.interval = opts.interval || 30;
if(app.enabled('rpcDebugLog')) {
opts.rpcDebugLog = true;
opts.rpcLogger = require('pomelo-logger').getLogger('rpc-debug', __filename);
}
return new Component(app, opts);
};
/**
* Remote component class
*
* @param {Object} app current application context
* @param {Object} opts construct parameters
*/
var Component = function(app, opts) {
this.app = app;
this.opts = opts;
};
var pro = Component.prototype;
pro.name = '__remote__';
/**
* Remote component lifecycle function
*
* @param {Function} cb
* @return {Void}
*/
pro.start = function(cb) {
this.opts.port = this.app.getCurServer().port;
this.remote = genRemote(this.app, this.opts);
this.remote.start();
process.nextTick(cb);
};
/**
* Remote component lifecycle function
*
* @param {Boolean} force whether stop the component immediately
* @param {Function} cb
* @return {Void}
*/
pro.stop = function(force, cb) {
this.remote.stop(force);
process.nextTick(cb);
};
/**
* Get remote paths from application
*
* @param {Object} app current application context
* @return {Array} paths
*
*/
var getRemotePaths = function(app) {
var paths = [];
var role;
// master server should not come here
if(app.isFrontend()) {
role = 'frontend';
} else {
role = 'backend';
}
var sysPath = pathUtil.getSysRemotePath(role), serverType = app.getServerType();
if(fs.existsSync(sysPath)) {
paths.push(pathUtil.remotePathRecord('sys', serverType, sysPath));
}
var userPath = pathUtil.getUserRemotePath(app.getBase(), serverType);
if(fs.existsSync(userPath)) {
paths.push(pathUtil.remotePathRecord('user', serverType, userPath));
}
return paths;
};
/**
* Generate remote server instance
*
* @param {Object} app current application context
* @param {Object} opts contructor parameters for rpc Server
* @return {Object} remote server instance
*/
var genRemote = function(app, opts) {
opts.paths = getRemotePaths(app);
opts.context = app;
if(!!opts.rpcServer) {
return opts.rpcServer.create(opts);
} else {
return RemoteServer.create(opts);
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 1 1 1 1 1 1 1 1 1 1 | /**
* Component for server starup.
*/
var Server = require('../server/server');
/**
* Component factory function
*
* @param {Object} app current application context
* @return {Object} component instance
*/
module.exports = function(app, opts) {
return new Component(app, opts);
};
/**
* Server component class
*
* @param {Object} app current application context
*/
var Component = function(app, opts) {
this.server = Server.create(app, opts);
};
var pro = Component.prototype;
pro.name = '__server__';
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
pro.start = function(cb) {
this.server.start();
process.nextTick(cb);
};
/**
* Component lifecycle callback
*
* @param {Function} cb
* @return {Void}
*/
Component.prototype.afterStart = function(cb) {
this.server.afterStart();
process.nextTick(cb);
};
/**
* Component lifecycle function
*
* @param {Boolean} force whether stop the component immediately
* @param {Function} cb
* @return {Void}
*/
pro.stop = function(force, cb) {
this.server.stop();
process.nextTick(cb);
};
/**
* Proxy server handle
*/
pro.handle = function(msg, session, cb) {
this.server.handle(msg, session, cb);
};
/**
* Proxy server global handle
*/
Component.prototype.globalHandle = function(msg, session, cb) {
this.server.globalHandle(msg, session, cb);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 1 1 1 1 | var SessionService = require('../common/service/sessionService');
module.exports = function(app, opts) {
var cmp = new Component(app, opts);
app.set('sessionService', cmp, true);
return cmp;
};
/**
* Session component. Manage sessions.
*
* @param {Object} app current application context
* @param {Object} opts attach parameters
*/
var Component = function(app, opts) {
opts = opts || {};
this.app = app;
this.service = new SessionService(opts);
var getFun = function(m) {
return (function() {
return function() {
return self.service[m].apply(self.service, arguments);
};
})();
};
// proxy the service methods except the lifecycle interfaces of component
var method, self = this;
for(var m in this.service) {
if(m !== 'start' && m !== 'stop') {
method = this.service[m];
if(typeof method === 'function') {
this[m] = getFun(m);
}
}
}
};
Component.prototype.name = '__session__';
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| hybridconnector.js | 31.58% | (18 / 57) | 0% | (0 / 8) | 0% | (0 / 5) | 31.58% | (18 / 57) | |
| hybridsocket.js | 30.65% | (19 / 62) | 0% | (0 / 18) | 0% | (0 / 9) | 30.65% | (19 / 62) | |
| mqttconnector.js | 32.73% | (18 / 55) | 0% | (0 / 12) | 0% | (0 / 13) | 32.73% | (18 / 55) | |
| mqttsocket.js | 28.57% | (10 / 35) | 0% | (0 / 6) | 0% | (0 / 5) | 28.57% | (10 / 35) | |
| sioconnector.js | 25.71% | (18 / 70) | 0% | (0 / 16) | 0% | (0 / 11) | 25.71% | (18 / 70) | |
| siosocket.js | 26.83% | (11 / 41) | 0% | (0 / 10) | 0% | (0 / 6) | 26.83% | (11 / 41) | |
| udpconnector.js | 36.51% | (23 / 63) | 0% | (0 / 12) | 0% | (0 / 8) | 36.51% | (23 / 63) | |
| udpsocket.js | 31.15% | (19 / 61) | 0% | (0 / 18) | 0% | (0 / 9) | 31.15% | (19 / 61) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var net = require('net');
var tls = require('tls');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var HybridSocket = require('./hybridsocket');
var Switcher = require('./hybrid/switcher');
var Handshake = require('./commands/handshake');
var Heartbeat = require('./commands/heartbeat');
var Kick = require('./commands/kick');
var coder = require('./common/coder');
var curId = 1;
/**
* Connector that manager low level connection and protocol bewteen server and client.
* Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.
*/
var Connector = function(port, host, opts) {
if (!(this instanceof Connector)) {
return new Connector(port, host, opts);
}
EventEmitter.call(this);
this.opts = opts || {};
this.port = port;
this.host = host;
this.useDict = opts.useDict;
this.useProtobuf = opts.useProtobuf;
this.handshake = new Handshake(opts);
this.heartbeat = new Heartbeat(opts);
this.distinctHost = opts.distinctHost;
this.ssl = opts.ssl;
this.switcher = null;
};
util.inherits(Connector, EventEmitter);
module.exports = Connector;
/**
* Start connector to listen the specified port
*/
Connector.prototype.start = function(cb) {
var app = require('../pomelo').app;
var self = this;
var gensocket = function(socket) {
var hybridsocket = new HybridSocket(curId++, socket);
hybridsocket.on('handshake', self.handshake.handle.bind(self.handshake, hybridsocket));
hybridsocket.on('heartbeat', self.heartbeat.handle.bind(self.heartbeat, hybridsocket));
hybridsocket.on('disconnect', self.heartbeat.clear.bind(self.heartbeat, hybridsocket.id));
hybridsocket.on('closing', Kick.handle.bind(null, hybridsocket));
self.emit('connection', hybridsocket);
};
this.connector = app.components.__connector__.connector;
this.dictionary = app.components.__dictionary__;
this.protobuf = app.components.__protobuf__;
this.decodeIO_protobuf = app.components.__decodeIO__protobuf__;
if(!this.ssl) {
this.listeningServer = net.createServer();
} else {
this.listeningServer = tls.createServer(this.ssl);
}
this.switcher = new Switcher(this.listeningServer, self.opts);
this.switcher.on('connection', function(socket) {
gensocket(socket);
});
if(!!this.distinctHost) {
this.listeningServer.listen(this.port, this.host);
} else {
this.listeningServer.listen(this.port);
}
process.nextTick(cb);
};
Connector.prototype.stop = function(force, cb) {
this.switcher.close();
this.listeningServer.close();
process.nextTick(cb);
};
Connector.decode = Connector.prototype.decode = coder.decode;
Connector.encode = Connector.prototype.encode = coder.encode;
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var EventEmitter = require('events').EventEmitter;
var handler = require('./common/handler');
var protocol = require('pomelo-protocol');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var Package = protocol.Package;
var ST_INITED = 0;
var ST_WAIT_ACK = 1;
var ST_WORKING = 2;
var ST_CLOSED = 3;
/**
* Socket class that wraps socket and websocket to provide unified interface for up level.
*/
var Socket = function(id, socket) {
EventEmitter.call(this);
this.id = id;
this.socket = socket;
if(!socket._socket) {
this.remoteAddress = {
ip: socket.address().address,
port: socket.address().port
};
} else {
this.remoteAddress = {
ip: socket._socket.remoteAddress,
port: socket._socket.remotePort
};
}
var self = this;
socket.once('close', this.emit.bind(this, 'disconnect'));
socket.on('error', this.emit.bind(this, 'error'));
socket.on('message', function(msg) {
if(msg) {
msg = Package.decode(msg);
handler(self, msg);
}
});
this.state = ST_INITED;
// TODO: any other events?
};
util.inherits(Socket, EventEmitter);
module.exports = Socket;
/**
* Send raw byte data.
*
* @api private
*/
Socket.prototype.sendRaw = function(msg) {
if(this.state !== ST_WORKING) {
return;
}
var self = this;
this.socket.send(msg, {binary: true}, function(err) {
if(!!err) {
logger.error('websocket send binary data failed: %j', err.stack);
return;
}
});
};
/**
* Send byte data package to client.
*
* @param {Buffer} msg byte data
*/
Socket.prototype.send = function(msg) {
if(msg instanceof String) {
msg = new Buffer(msg);
} else if(!(msg instanceof Buffer)) {
msg = new Buffer(JSON.stringify(msg));
}
this.sendRaw(Package.encode(Package.TYPE_DATA, msg));
};
/**
* Send byte data packages to client in batch.
*
* @param {Buffer} msgs byte data
*/
Socket.prototype.sendBatch = function(msgs) {
var rs = [];
for(var i=0; i<msgs.length; i++) {
var src = Package.encode(Package.TYPE_DATA, msgs[i]);
rs.push(src);
}
this.sendRaw(Buffer.concat(rs));
};
/**
* Send message to client no matter whether handshake.
*
* @api private
*/
Socket.prototype.sendForce = function(msg) {
if(this.state === ST_CLOSED) {
return;
}
this.socket.send(msg, {binary: true});
};
/**
* Response handshake request
*
* @api private
*/
Socket.prototype.handshakeResponse = function(resp) {
if(this.state !== ST_INITED) {
return;
}
this.socket.send(resp, {binary: true});
this.state = ST_WAIT_ACK;
};
/**
* Close the connection.
*
* @api private
*/
Socket.prototype.disconnect = function() {
if(this.state === ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.socket.emit('close');
this.socket.close();
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var EventEmitter = require('events').EventEmitter;
var mqtt = require('mqtt');
var constants = require('../util/constants');
var MQTTSocket = require('./mqttsocket');
var Adaptor = require('./mqtt/mqttadaptor');
var generate = require('./mqtt/generate');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var curId = 1;
/**
* Connector that manager low level connection and protocol bewteen server and client.
* Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.
*/
var Connector = function(port, host, opts) {
if (!(this instanceof Connector)) {
return new Connector(port, host, opts);
}
EventEmitter.call(this);
this.port = port;
this.host = host;
this.opts = opts || {};
this.adaptor = new Adaptor(this.opts);
};
util.inherits(Connector, EventEmitter);
module.exports = Connector;
/**
* Start connector to listen the specified port
*/
Connector.prototype.start = function(cb) {
var self = this;
this.mqttServer = mqtt.createServer();
this.mqttServer.on('client', function(client) {
client.on('error', function(err) {
client.stream.destroy();
});
client.on('close', function() {
client.stream.destroy();
});
client.on('disconnect', function(packet) {
client.stream.destroy();
});
if(self.opts.disconnectOnTimeout) {
var timeout = self.opts.timeout * 1000 || constants.TIME.DEFAULT_MQTT_HEARTBEAT_TIMEOUT;
client.stream.setTimeout(timeout,function() {
client.emit('close');
});
}
client.on('connect', function(packet) {
client.connack({returnCode: 0});
var mqttsocket = new MQTTSocket(curId++, client, self.adaptor);
self.emit('connection', mqttsocket);
});
});
this.mqttServer.listen(this.port);
process.nextTick(cb);
};
Connector.prototype.stop = function() {
this.mqttServer.close();
process.exit(0);
};
var composeResponse = function(msgId, route, msgBody) {
return {
id: msgId,
body: msgBody
};
};
var composePush = function(route, msgBody) {
var msg = generate.publish(msgBody);
if(!msg) {
logger.error('invalid mqtt publish message: %j', msgBody);
}
return msg;
};
Connector.prototype.encode = function(reqId, route, msgBody) {
if (!!reqId) {
return composeResponse(reqId, route, msgBody);
} else {
return composePush(route, msgBody);
}
};
Connector.prototype.close = function() {
this.mqttServer.close();
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var EventEmitter = require('events').EventEmitter;
var ST_INITED = 1;
var ST_CLOSED = 2;
/**
* Socket class that wraps socket and websocket to provide unified interface for up level.
*/
var Socket = function(id, socket, adaptor) {
EventEmitter.call(this);
this.id = id;
this.socket = socket;
this.remoteAddress = {
ip: socket.stream.remoteAddress,
port: socket.stream.remotePort
};
this.adaptor = adaptor;
var self = this;
socket.on('close', this.emit.bind(this, 'disconnect'));
socket.on('error', this.emit.bind(this, 'disconnect'));
socket.on('disconnect', this.emit.bind(this, 'disconnect'));
socket.on('pingreq', function(packet) {
socket.pingresp();
});
socket.on('subscribe', this.adaptor.onSubscribe.bind(this.adaptor, this));
socket.on('publish', this.adaptor.onPublish.bind(this.adaptor, this));
this.state = ST_INITED;
// TODO: any other events?
};
util.inherits(Socket, EventEmitter);
module.exports = Socket;
Socket.prototype.send = function(msg) {
if(this.state !== ST_INITED) {
return;
}
if(msg instanceof Buffer) {
// if encoded, send directly
this.socket.stream.write(msg);
} else {
this.adaptor.publish(this, msg);
}
};
Socket.prototype.sendBatch = function(msgs) {
for(var i = 0, l = msgs.length; i<l; i++) {
this.send(msgs[i]);
}
};
Socket.prototype.disconnect = function() {
if(this.state === ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.socket.stream.destroy();
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var EventEmitter = require('events').EventEmitter;
var httpServer = require('http').createServer();
var SioSocket = require('./siosocket');
var PKG_ID_BYTES = 4;
var PKG_ROUTE_LENGTH_BYTES = 1;
var PKG_HEAD_BYTES = PKG_ID_BYTES + PKG_ROUTE_LENGTH_BYTES;
var curId = 1;
/**
* Connector that manager low level connection and protocol bewteen server and client.
* Develper can provide their own connector to switch the low level prototol, such as tcp or probuf.
*/
var Connector = function(port, host, opts) {
if (!(this instanceof Connector)) {
return new Connector(port, host, opts);
}
EventEmitter.call(this);
this.port = port;
this.host = host;
this.opts = opts;
this.heartbeats = opts.heartbeats || true;
this.closeTimeout = opts.closeTimeout || 60;
this.heartbeatTimeout = opts.heartbeatTimeout || 60;
this.heartbeatInterval = opts.heartbeatInterval || 25;
};
util.inherits(Connector, EventEmitter);
module.exports = Connector;
/**
* Start connector to listen the specified port
*/
Connector.prototype.start = function(cb) {
var self = this;
// issue https://github.com/NetEase/pomelo-cn/issues/174
var opts = {}
if(!!this.opts) {
opts = this.opts;
}
else {
opts = {
transports: [
'websocket', 'polling-xhr', 'polling-jsonp', 'polling'
]
};
}
var sio = require('socket.io')(httpServer, opts);
var port = this.port;
httpServer.listen(port, function () {
console.log('sio Server listening at port %d', port);
});
sio.set('path', '/socket.io');
sio.set('transports', this.opts.transports);
sio.set('close timeout', this.closeTimeout);
sio.set('heartbeat timeout', this.heartbeatTimeout);
sio.set('heartbeat interval', this.heartbeatInterval);
sio.set('heartbeats', this.heartbeats);
sio.set('log level', 1);
sio.on('connection', function (socket) {
// this.wsocket.sockets.on('connection', function (socket) {
var siosocket = new SioSocket(curId++, socket);
self.emit('connection', siosocket);
siosocket.on('closing', function(reason) {
siosocket.send({route: 'onKick', reason: reason});
});
});
process.nextTick(cb);
};
/**
* Stop connector
*/
Connector.prototype.stop = function(force, cb) {
this.wsocket.server.close();
process.nextTick(cb);
};
Connector.encode = Connector.prototype.encode = function(reqId, route, msg) {
if(reqId) {
return composeResponse(reqId, route, msg);
} else {
return composePush(route, msg);
}
};
/**
* Decode client message package.
*
* Package format:
* message id: 4bytes big-endian integer
* route length: 1byte
* route: route length bytes
* body: the rest bytes
*
* @param {String} data socket.io package from client
* @return {Object} message object
*/
Connector.decode = Connector.prototype.decode = function(msg) {
var index = 0;
var id = parseIntField(msg, index, PKG_ID_BYTES);
index += PKG_ID_BYTES;
var routeLen = parseIntField(msg, index, PKG_ROUTE_LENGTH_BYTES);
var route = msg.substr(PKG_HEAD_BYTES, routeLen);
var body = msg.substr(PKG_HEAD_BYTES + routeLen);
return {
id: id,
route: route,
body: JSON.parse(body)
};
};
var composeResponse = function(msgId, route, msgBody) {
return {
id: msgId,
body: msgBody
};
};
var composePush = function(route, msgBody) {
return JSON.stringify({route: route, body: msgBody});
};
var parseIntField = function(str, offset, len) {
var res = 0;
for(var i=0; i<len; i++) {
if(i > 0) {
res <<= 8;
}
res |= str.charCodeAt(offset + i) & 0xff;
}
return res;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 1 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var EventEmitter = require('events').EventEmitter;
var ST_INITED = 0;
var ST_CLOSED = 1;
/**
* Socket class that wraps socket.io socket to provide unified interface for up level.
*/
var Socket = function(id, socket) {
EventEmitter.call(this);
this.id = id;
this.socket = socket;
this.remoteAddress = {
ip: socket.handshake.address.address,
port: socket.handshake.address.port
};
var self = this;
socket.on('disconnect', this.emit.bind(this, 'disconnect'));
socket.on('error', this.emit.bind(this, 'error'));
socket.on('message', function(msg) {
self.emit('message', msg);
});
this.state = ST_INITED;
// TODO: any other events?
};
util.inherits(Socket, EventEmitter);
module.exports = Socket;
Socket.prototype.send = function(msg) {
if(this.state !== ST_INITED) {
return;
}
if(typeof msg !== 'string') {
msg = JSON.stringify(msg);
}
this.socket.send(msg);
};
Socket.prototype.disconnect = function() {
if(this.state === ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.socket.disconnect();
};
Socket.prototype.sendBatch = function(msgs) {
this.send(encodeBatch(msgs));
};
/**
* Encode batch msg to client
*/
var encodeBatch = function(msgs){
var res = '[', msg;
for(var i=0, l=msgs.length; i<l; i++) {
if(i > 0) {
res += ',';
}
msg = msgs[i];
if(typeof msg === 'string') {
res += msg;
} else {
res += JSON.stringify(msg);
}
}
res += ']';
return res;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var net = require('net');
var util = require('util');
var dgram = require("dgram");
var utils = require('../util/utils');
var Constants = require('../util/constants');
var UdpSocket = require('./udpsocket');
var Kick = require('./commands/kick');
var Handshake = require('./commands/handshake');
var Heartbeat = require('./commands/heartbeat');
var protocol = require('pomelo-protocol');
var Package = protocol.Package;
var Message = protocol.Message;
var coder = require('./common/coder');
var EventEmitter = require('events').EventEmitter;
var curId = 1;
var Connector = function(port, host, opts) {
if (!(this instanceof Connector)) {
return new Connector(port, host, opts);
}
EventEmitter.call(this);
this.opts = opts || {};
this.type = opts.udpType || 'udp4';
this.handshake = new Handshake(opts);
if(!opts.heartbeat) {
opts.heartbeat = Constants.TIME.DEFAULT_UDP_HEARTBEAT_TIME;
opts.timeout = Constants.TIME.DEFAULT_UDP_HEARTBEAT_TIMEOUT;
}
this.heartbeat = new Heartbeat(utils.extends(opts, {disconnectOnTimeout: true}));
this.clients = {};
this.host = host;
this.port = port;
};
util.inherits(Connector, EventEmitter);
module.exports = Connector;
Connector.prototype.start = function(cb) {
var self = this;
this.tcpServer = net.createServer();
this.socket = dgram.createSocket(this.type, function(msg, peer) {
var key = genKey(peer);
if(!self.clients[key]) {
var udpsocket = new UdpSocket(curId++, self.socket, peer);
self.clients[key] = udpsocket;
udpsocket.on('handshake',
self.handshake.handle.bind(self.handshake, udpsocket));
udpsocket.on('heartbeat',
self.heartbeat.handle.bind(self.heartbeat, udpsocket));
udpsocket.on('disconnect',
self.heartbeat.clear.bind(self.heartbeat, udpsocket.id));
udpsocket.on('disconnect', function() {
delete self.clients[genKey(udpsocket.peer)];
});
udpsocket.on('closing', Kick.handle.bind(null, udpsocket));
self.emit('connection', udpsocket);
}
});
this.socket.on('message', function(data, peer) {
var socket = self.clients[genKey(peer)];
if(!!socket) {
socket.emit('package', data);
}
});
this.socket.on('error', function(err) {
logger.error('udp socket encounters with error: %j', err.stack);
return;
});
this.socket.bind(this.port, this.host);
this.tcpServer.listen(this.port);
process.nextTick(cb);
};
Connector.decode = Connector.prototype.decode = coder.decode;
Connector.encode = Connector.prototype.encode = coder.encode;
Connector.prototype.stop = function(force, cb) {
this.socket.close();
process.nextTick(cb);
};
var genKey = function(peer) {
return peer.address + ":" + peer.port;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var util = require('util');
var handler = require('./common/handler');
var protocol = require('pomelo-protocol');
var Package = protocol.Package;
var EventEmitter = require('events').EventEmitter;
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var ST_INITED = 0;
var ST_WAIT_ACK = 1;
var ST_WORKING = 2;
var ST_CLOSED = 3;
var Socket = function(id, socket, peer) {
EventEmitter.call(this);
this.id = id;
this.socket = socket;
this.peer = peer;
this.host = peer.address;
this.port = peer.port;
this.remoteAddress = {
ip: this.host,
port: this.port
};
var self = this;
this.on('package', function(pkg) {
if(!!pkg) {
pkg = Package.decode(pkg);
handler(self, pkg);
}
});
this.state = ST_INITED;
};
util.inherits(Socket, EventEmitter);
module.exports = Socket;
/**
* Send byte data package to client.
*
* @param {Buffer} msg byte data
*/
Socket.prototype.send = function(msg) {
if(this.state !== ST_WORKING) {
return;
}
if(msg instanceof String) {
msg = new Buffer(msg);
} else if(!(msg instanceof Buffer)) {
msg = new Buffer(JSON.stringify(msg));
}
this.sendRaw(Package.encode(Package.TYPE_DATA, msg));
};
Socket.prototype.sendRaw = function(msg) {
this.socket.send(msg, 0, msg.length, this.port, this.host, function(err, bytes) {
if(!!err) {
logger.error('send msg to remote with err: %j', err.stack);
return;
}
});
};
Socket.prototype.sendForce = function(msg) {
if(this.state === ST_CLOSED) {
return;
}
this.sendRaw(msg);
};
Socket.prototype.handshakeResponse = function(resp) {
if(this.state !== ST_INITED) {
return;
}
this.sendRaw(resp);
this.state = ST_WAIT_ACK;
};
Socket.prototype.sendBatch = function(msgs) {
if(this.state !== ST_WORKING) {
return;
}
var rs = [];
for(var i=0; i<msgs.length; i++) {
var src = Package.encode(Package.TYPE_DATA, msgs[i]);
rs.push(src);
}
this.sendRaw(Buffer.concat(rs));
};
Socket.prototype.disconnect = function() {
if(this.state === ST_CLOSED) {
return;
}
this.state = ST_CLOSED;
this.emit('disconnect', 'the connection is disconnected.');
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| handshake.js | 15.71% | (11 / 70) | 0% | (0 / 41) | 0% | (0 / 10) | 15.71% | (11 / 70) | |
| heartbeat.js | 17.5% | (7 / 40) | 0% | (0 / 18) | 0% | (0 / 5) | 17.5% | (7 / 40) | |
| kick.js | 40% | (2 / 5) | 0% | (0 / 2) | 0% | (0 / 1) | 40% | (2 / 5) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 1 1 1 1 1 1 1 1 1 1 1 | var pomelo = require('../../pomelo');
var Package = require('pomelo-protocol').Package;
var CODE_OK = 200;
var CODE_USE_ERROR = 500;
var CODE_OLD_CLIENT = 501;
/**
* Process the handshake request.
*
* @param {Object} opts option parameters
* opts.handshake(msg, cb(err, resp)) handshake callback. msg is the handshake message from client.
* opts.hearbeat heartbeat interval (level?)
* opts.version required client level
*/
var Command = function(opts) {
opts = opts || {};
this.userHandshake = opts.handshake;
if(opts.heartbeat) {
this.heartbeatSec = opts.heartbeat;
this.heartbeat = opts.heartbeat * 1000;
}
this.checkClient = opts.checkClient;
this.useDict = opts.useDict;
this.useProtobuf = opts.useProtobuf;
this.useCrypto = opts.useCrypto;
};
module.exports = Command;
Command.prototype.handle = function(socket, msg) {
if(!msg.sys) {
processError(socket, CODE_USE_ERROR);
return;
}
if(typeof this.checkClient === 'function') {
if(!msg || !msg.sys || !this.checkClient(msg.sys.type, msg.sys.version)) {
processError(socket, CODE_OLD_CLIENT);
return;
}
}
var opts = {
heartbeat : setupHeartbeat(this)
};
if(this.useDict) {
var dictVersion = pomelo.app.components.__dictionary__.getVersion();
if(!msg.sys.dictVersion || msg.sys.dictVersion !== dictVersion){
// may be deprecated in future
opts.dict = pomelo.app.components.__dictionary__.getDict();
opts.routeToCode = pomelo.app.components.__dictionary__.getDict();
opts.codeToRoute = pomelo.app.components.__dictionary__.getAbbrs();
opts.dictVersion = dictVersion;
}
opts.useDict = true;
}
if(this.useProtobuf) {
var protoVersion = pomelo.app.components.__protobuf__.getVersion();
if(!msg.sys.protoVersion || msg.sys.protoVersion !== protoVersion){
opts.protos = pomelo.app.components.__protobuf__.getProtos();
}
opts.useProto = true;
}
if(!!pomelo.app.components.__decodeIO__protobuf__) {
if(!!this.useProtobuf) {
throw new Error('protobuf can not be both used in the same project.');
}
var version = pomelo.app.components.__decodeIO__protobuf__.getVersion();
if(!msg.sys.protoVersion || msg.sys.protoVersion < version) {
opts.protos = pomelo.app.components.__decodeIO__protobuf__.getProtos();
}
opts.useProto = true;
}
if(this.useCrypto) {
pomelo.app.components.__connector__.setPubKey(socket.id, msg.sys.rsa);
}
if(typeof this.userHandshake === 'function') {
this.userHandshake(msg, function(err, resp) {
if(err) {
process.nextTick(function() {
processError(socket, CODE_USE_ERROR);
});
return;
}
process.nextTick(function() {
response(socket, opts, resp);
});
}, socket);
return;
}
process.nextTick(function() {
response(socket, opts);
});
};
var setupHeartbeat = function(self) {
return self.heartbeatSec;
};
var response = function(socket, sys, resp) {
var res = {
code: CODE_OK,
sys: sys
};
if(resp) {
res.user = resp;
}
socket.handshakeResponse(Package.encode(Package.TYPE_HANDSHAKE, new Buffer(JSON.stringify(res))));
};
var processError = function(socket, code) {
var res = {
code: code
};
socket.sendForce(Package.encode(Package.TYPE_HANDSHAKE, new Buffer(JSON.stringify(res))));
process.nextTick(function() {
socket.disconnect();
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 1 1 1 1 1 1 1 | var Package = require('pomelo-protocol').Package;
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* Process heartbeat request.
*
* @param {Object} opts option request
* opts.heartbeat heartbeat interval
*/
var Command = function(opts) {
opts = opts || {};
this.heartbeat = null;
this.timeout = null;
this.disconnectOnTimeout = opts.disconnectOnTimeout;
if(opts.heartbeat) {
this.heartbeat = opts.heartbeat * 1000; // heartbeat interval
this.timeout = opts.timeout * 1000 || this.heartbeat * 2; // max heartbeat message timeout
this.disconnectOnTimeout = true;
}
this.timeouts = {};
this.clients = {};
};
module.exports = Command;
Command.prototype.handle = function(socket) {
if(!this.heartbeat) {
// no heartbeat setting
return;
}
var self = this;
if(!this.clients[socket.id]) {
// clear timers when socket disconnect or error
this.clients[socket.id] = 1;
socket.once('disconnect', clearTimers.bind(null, this, socket.id));
socket.once('error', clearTimers.bind(null, this, socket.id));
}
// clear timeout timer
if(self.disconnectOnTimeout) {
this.clear(socket.id);
}
socket.sendRaw(Package.encode(Package.TYPE_HEARTBEAT));
if(self.disconnectOnTimeout) {
self.timeouts[socket.id] = setTimeout(function() {
logger.info('client %j heartbeat timeout.', socket.id);
socket.disconnect();
}, self.timeout);
}
};
Command.prototype.clear = function(id) {
var tid = this.timeouts[id];
if(tid) {
clearTimeout(tid);
delete this.timeouts[id];
}
};
var clearTimers = function(self, id) {
delete self.clients[id];
var tid = self.timeouts[id];
if(tid) {
clearTimeout(tid);
delete self.timeouts[id];
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | 1 1 | var Package = require('pomelo-protocol').Package;
module.exports.handle = function(socket, reason) {
// websocket close code 1000 would emit when client close the connection
if(typeof reason === 'string') {
var res = {
reason: reason
};
socket.sendRaw(Package.encode(Package.TYPE_KICK, new Buffer(JSON.stringify(res))));
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| coder.js | 17.65% | (9 / 51) | 0% | (0 / 39) | 0% | (0 / 5) | 17.65% | (9 / 51) | |
| handler.js | 47.37% | (18 / 38) | 0% | (0 / 10) | 0% | (0 / 5) | 47.37% | (18 / 38) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 1 1 1 1 1 1 1 1 1 | var Message = require('pomelo-protocol').Message;
var Constants = require('../../util/constants');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var encode = function(reqId, route, msg) {
if(!!reqId) {
return composeResponse(this, reqId, route, msg);
} else {
return composePush(this, route, msg);
}
};
var decode = function(msg) {
msg = Message.decode(msg.body);
var route = msg.route;
// decode use dictionary
if(!!msg.compressRoute) {
if(!!this.connector.useDict) {
var abbrs = this.dictionary.getAbbrs();
if(!abbrs[route]) {
logger.error('dictionary error! no abbrs for route : %s', route);
return null;
}
route = msg.route = abbrs[route];
} else {
logger.error('fail to uncompress route code for msg: %j, server not enable dictionary.', msg);
return null;
}
}
// decode use protobuf
if(!!this.protobuf && !!this.protobuf.getProtos().client[route]) {
msg.body = this.protobuf.decode(route, msg.body);
} else if(!!this.decodeIO_protobuf && !!this.decodeIO_protobuf.check(Constants.RESERVED.CLIENT, route)) {
msg.body = this.decodeIO_protobuf.decode(route, msg.body);
} else {
try {
msg.body = JSON.parse(msg.body.toString('utf8'));
} catch (ex) {
msg.body = {};
}
}
return msg;
};
var composeResponse = function(server, msgId, route, msgBody) {
if(!msgId || !route || !msgBody) {
return null;
}
msgBody = encodeBody(server, route, msgBody);
return Message.encode(msgId, Message.TYPE_RESPONSE, 0, null, msgBody);
};
var composePush = function(server, route, msgBody) {
if(!route || !msgBody){
return null;
}
msgBody = encodeBody(server, route, msgBody);
// encode use dictionary
var compressRoute = 0;
if(!!server.dictionary) {
var dict = server.dictionary.getDict();
if(!!server.connector.useDict && !!dict[route]) {
route = dict[route];
compressRoute = 1;
}
}
return Message.encode(0, Message.TYPE_PUSH, compressRoute, route, msgBody);
};
var encodeBody = function(server, route, msgBody) {
// encode use protobuf
if(!!server.protobuf && !!server.protobuf.getProtos().server[route]) {
msgBody = server.protobuf.encode(route, msgBody);
} else if(!!server.decodeIO_protobuf && !!server.decodeIO_protobuf.check(Constants.RESERVED.SERVER, route)) {
msgBody = server.decodeIO_protobuf.encode(route, msgBody);
} else {
msgBody = new Buffer(JSON.stringify(msgBody), 'utf8');
}
return msgBody;
};
module.exports = {
encode: encode,
decode: decode
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var protocol = require('pomelo-protocol');
var Package = protocol.Package;
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var handlers = {};
var ST_INITED = 0;
var ST_WAIT_ACK = 1;
var ST_WORKING = 2;
var ST_CLOSED = 3;
var handleHandshake = function(socket, pkg) {
if(socket.state !== ST_INITED) {
return;
}
try {
socket.emit('handshake', JSON.parse(protocol.strdecode(pkg.body)));
} catch (ex) {
socket.emit('handshake', {});
}
};
var handleHandshakeAck = function(socket, pkg) {
if(socket.state !== ST_WAIT_ACK) {
return;
}
socket.state = ST_WORKING;
socket.emit('heartbeat');
};
var handleHeartbeat = function(socket, pkg) {
if(socket.state !== ST_WORKING) {
return;
}
socket.emit('heartbeat');
};
var handleData = function(socket, pkg) {
if(socket.state !== ST_WORKING) {
return;
}
socket.emit('message', pkg);
};
handlers[Package.TYPE_HANDSHAKE] = handleHandshake;
handlers[Package.TYPE_HANDSHAKE_ACK] = handleHandshakeAck;
handlers[Package.TYPE_HEARTBEAT] = handleHeartbeat;
handlers[Package.TYPE_DATA] = handleData;
var handle = function(socket, pkg) {
var handler = handlers[pkg.type];
if(!!handler) {
handler(socket, pkg);
} else {
logger.error('could not find handle invalid data package.');
socket.disconnect();
}
};
module.exports = handle;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| switcher.js | 29.82% | (17 / 57) | 0% | (0 / 14) | 0% | (0 / 9) | 29.82% | (17 / 57) | |
| tcpprocessor.js | 52.17% | (12 / 23) | 0% | (0 / 4) | 0% | (0 / 3) | 52.17% | (12 / 23) | |
| tcpsocket.js | 18.45% | (19 / 103) | 0% | (0 / 43) | 0% | (0 / 9) | 18.45% | (19 / 103) | |
| wsprocessor.js | 36.67% | (11 / 30) | 0% | (0 / 6) | 0% | (0 / 4) | 36.67% | (11 / 30) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var EventEmitter = require('events').EventEmitter;
var util = require('util');
var WSProcessor = require('./wsprocessor');
var TCPProcessor = require('./tcpprocessor');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var HTTP_METHODS = [
'GET', 'POST', 'DELETE', 'PUT', 'HEAD'
];
var ST_STARTED = 1;
var ST_CLOSED = 2;
var DEFAULT_TIMEOUT = 90;
/**
* Switcher for tcp and websocket protocol
*
* @param {Object} server tcp server instance from node.js net module
*/
var Switcher = function(server, opts) {
EventEmitter.call(this);
this.server = server;
this.wsprocessor = new WSProcessor();
this.tcpprocessor = new TCPProcessor(opts.closeMethod);
this.id = 1;
this.timeout = (opts.timeout || DEFAULT_TIMEOUT) * 1000;
this.setNoDelay = opts.setNoDelay;
if (!opts.ssl) {
this.server.on('connection', this.newSocket.bind(this));
} else {
this.server.on('secureConnection', this.newSocket.bind(this));
this.server.on('clientError', function(e, tlsSo) {
logger.warn('an ssl error occured before handshake established: ', e);
tlsSo.destroy();
});
}
this.wsprocessor.on('connection', this.emit.bind(this, 'connection'));
this.tcpprocessor.on('connection', this.emit.bind(this, 'connection'));
this.state = ST_STARTED;
};
util.inherits(Switcher, EventEmitter);
module.exports = Switcher;
Switcher.prototype.newSocket = function(socket) {
if(this.state !== ST_STARTED) {
return;
}
socket.setTimeout(this.timeout, function() {
logger.warn('connection is timeout without communication, the remote ip is %s && port is %s',
socket.remoteAddress, socket.remotePort);
socket.destroy();
});
var self = this;
socket.once('data', function(data) {
// FIXME: handle incomplete HTTP method
if(isHttp(data)) {
processHttp(self, self.wsprocessor, socket, data);
} else {
if(!!self.setNoDelay) {
socket.setNoDelay(true);
}
processTcp(self, self.tcpprocessor, socket, data);
}
});
};
Switcher.prototype.close = function() {
if(this.state !== ST_STARTED) {
return;
}
this.state = ST_CLOSED;
this.wsprocessor.close();
this.tcpprocessor.close();
};
var isHttp = function(data) {
var head = data.toString('utf8', 0, 4);
for(var i=0, l=HTTP_METHODS.length; i<l; i++) {
if(head.indexOf(HTTP_METHODS[i]) === 0) {
return true;
}
}
return false;
};
var processHttp = function(switcher, processor, socket, data) {
processor.add(socket, data);
};
var processTcp = function(switcher, processor, socket, data) {
processor.add(socket, data);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 1 1 1 1 1 1 1 1 1 1 1 1 | var EventEmitter = require('events').EventEmitter;
var util = require('util');
var utils = require('../../util/utils');
var TcpSocket = require('./tcpsocket');
var ST_STARTED = 1;
var ST_CLOSED = 2;
// private protocol, no need exports
var HEAD_SIZE = 4;
/**
* websocket protocol processor
*/
var Processor = function(closeMethod) {
EventEmitter.call(this);
this.closeMethod = closeMethod;
this.state = ST_STARTED;
};
util.inherits(Processor, EventEmitter);
module.exports = Processor;
Processor.prototype.add = function(socket, data) {
if(this.state !== ST_STARTED) {
return;
}
var tcpsocket = new TcpSocket(socket, {headSize: HEAD_SIZE,
headHandler: utils.headHandler,
closeMethod: this.closeMethod});
this.emit('connection', tcpsocket);
socket.emit('data', data);
};
Processor.prototype.close = function() {
if(this.state !== ST_STARTED) {
return;
}
this.state = ST_CLOSED;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var Stream = require('stream');
var util = require('util');
var protocol = require('pomelo-protocol');
var Package = protocol.Package;
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* Work states
*/
var ST_HEAD = 1; // wait for head
var ST_BODY = 2; // wait for body
var ST_CLOSED = 3; // closed
/**
* Tcp socket wrapper with package compositing.
* Collect the package from socket and emit a completed package with 'data' event.
* Uniform with ws.WebSocket interfaces.
*
* @param {Object} socket origin socket from node.js net module
* @param {Object} opts options parameter.
* opts.headSize size of package head
* opts.headHandler(headBuffer) handler for package head. caculate and return body size from head data.
*/
var Socket = function(socket, opts) {
if(!(this instanceof Socket)) {
return new Socket(socket, opts);
}
if(!socket || !opts) {
throw new Error('invalid socket or opts');
}
if(!opts.headSize || typeof opts.headHandler !== 'function') {
throw new Error('invalid opts.headSize or opts.headHandler');
}
// stream style interfaces.
// TODO: need to port to stream2 after node 0.9
Stream.call(this);
this.readable = true;
this.writeable = true;
this._socket = socket;
this.headSize = opts.headSize;
this.closeMethod = opts.closeMethod;
this.headBuffer = new Buffer(opts.headSize);
this.headHandler = opts.headHandler;
this.headOffset = 0;
this.packageOffset = 0;
this.packageSize = 0;
this.packageBuffer = null;
// bind event form the origin socket
this._socket.on('data', ondata.bind(null, this));
this._socket.on('end', onend.bind(null, this));
this._socket.on('error', this.emit.bind(this, 'error'));
this._socket.on('close', this.emit.bind(this, 'close'));
this.state = ST_HEAD;
};
util.inherits(Socket, Stream);
module.exports = Socket;
Socket.prototype.send = function(msg, encode, cb) {
this._socket.write(msg, encode, cb);
};
Socket.prototype.close = function() {
if(!!this.closeMethod && this.closeMethod === 'end') {
this._socket.end();
} else {
try {
this._socket.destroy();
} catch (e) {
logger.error('socket close with destroy error: %j', e.stack);
}
}
};
var ondata = function(socket, chunk) {
if(socket.state === ST_CLOSED) {
throw new Error('socket has closed');
}
if(typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {
throw new Error('invalid data');
}
if(typeof chunk === 'string') {
chunk = new Buffer(chunk, 'utf8');
}
var offset = 0, end = chunk.length;
while(offset < end && socket.state !== ST_CLOSED) {
if(socket.state === ST_HEAD) {
offset = readHead(socket, chunk, offset);
}
if(socket.state === ST_BODY) {
offset = readBody(socket, chunk, offset);
}
}
return true;
};
var onend = function(socket, chunk) {
if(chunk) {
socket._socket.write(chunk);
}
socket.state = ST_CLOSED;
reset(socket);
socket.emit('end');
};
/**
* Read head segment from data to socket.headBuffer.
*
* @param {Object} socket Socket instance
* @param {Object} data Buffer instance
* @param {Number} offset offset read star from data
* @return {Number} new offset of data after read
*/
var readHead = function(socket, data, offset) {
var hlen = socket.headSize - socket.headOffset;
var dlen = data.length - offset;
var len = Math.min(hlen, dlen);
var dend = offset + len;
data.copy(socket.headBuffer, socket.headOffset, offset, dend);
socket.headOffset += len;
if(socket.headOffset === socket.headSize) {
// if head segment finished
var size = socket.headHandler(socket.headBuffer);
if(size < 0) {
throw new Error('invalid body size: ' + size);
}
// check if header contains a valid type
if(checkTypeData(socket.headBuffer[0])) {
socket.packageSize = size + socket.headSize;
socket.packageBuffer = new Buffer(socket.packageSize);
socket.headBuffer.copy(socket.packageBuffer, 0, 0, socket.headSize);
socket.packageOffset = socket.headSize;
socket.state = ST_BODY;
} else {
dend = data.length;
logger.error('close the connection with invalid head message, the remote ip is %s && port is %s && message is %j', socket._socket.remoteAddress, socket._socket.remotePort, data);
socket.close();
}
}
return dend;
};
/**
* Read body segment from data buffer to socket.packageBuffer;
*
* @param {Object} socket Socket instance
* @param {Object} data Buffer instance
* @param {Number} offset offset read star from data
* @return {Number} new offset of data after read
*/
var readBody = function(socket, data, offset) {
var blen = socket.packageSize - socket.packageOffset;
var dlen = data.length - offset;
var len = Math.min(blen, dlen);
var dend = offset + len;
data.copy(socket.packageBuffer, socket.packageOffset, offset, dend);
socket.packageOffset += len;
if(socket.packageOffset === socket.packageSize) {
// if all the package finished
var buffer = socket.packageBuffer;
socket.emit('message', buffer);
reset(socket);
}
return dend;
};
var reset = function(socket) {
socket.headOffset = 0;
socket.packageOffset = 0;
socket.packageSize = 0;
socket.packageBuffer = null;
socket.state = ST_HEAD;
};
var checkTypeData = function(data) {
return data === Package.TYPE_HANDSHAKE || data === Package.TYPE_HANDSHAKE_ACK || data === Package.TYPE_HEARTBEAT || data === Package.TYPE_DATA || data === Package.TYPE_KICK;
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 1 1 1 1 1 1 1 1 1 1 1 | var HttpServer = require('http').Server;
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var WebSocketServer = require('ws').Server;
var ST_STARTED = 1;
var ST_CLOSED = 2;
/**
* websocket protocol processor
*/
var Processor = function() {
EventEmitter.call(this);
this.httpServer = new HttpServer();
var self = this;
this.wsServer = new WebSocketServer({server: this.httpServer});
this.wsServer.on('connection', function(socket) {
// emit socket to outside
self.emit('connection', socket);
});
this.state = ST_STARTED;
};
util.inherits(Processor, EventEmitter);
module.exports = Processor;
Processor.prototype.add = function(socket, data) {
if(this.state !== ST_STARTED) {
return;
}
this.httpServer.emit('connection', socket);
if(typeof socket.ondata === 'function') {
// compatible with stream2
socket.ondata(data, 0, data.length);
} else {
// compatible with old stream
socket.emit('data', data);
}
};
Processor.prototype.close = function() {
if(this.state !== ST_STARTED) {
return;
}
this.state = ST_CLOSED;
this.wsServer.close();
this.wsServer = null;
this.httpServer = null;
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| generate.js | 8.43% | (7 / 83) | 0% | (0 / 54) | 0% | (0 / 5) | 11.29% | (7 / 62) | |
| mqttadaptor.js | 16.67% | (6 / 36) | 0% | (0 / 12) | 0% | (0 / 5) | 16.67% | (6 / 36) | |
| protocol.js | 100% | (20 / 20) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (20 / 20) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 1 1 1 1 1 1 1 | var protocol = require('./protocol');
var crypto = require('crypto');
/* TODO: consider rewriting these functions using buffers instead
* of arrays
*/
/* Publish */
module.exports.publish = function(opts) {
opts = opts || {};
var dup = opts.dup ? protocol.DUP_MASK : 0;
var qos = opts.qos || 0;
var retain = opts.retain ? protocol.RETAIN_MASK : 0;
var topic = opts.topic;
var payload = opts.payload || new Buffer(0);
var id = (typeof opts.messageId === 'undefined') ? randint() : opts.messageId;
var packet = {header: 0, payload: []};
/* Check required fields */
if (typeof topic !== 'string' || topic.length <= 0) return null;
/* if payload is a string, we'll convert it into a buffer */
if(typeof payload == 'string') {
payload = new Buffer(payload);
}
/* accepting only a buffer for payload */
if (!Buffer.isBuffer(payload)) return null;
if (typeof qos !== 'number' || qos < 0 || qos > 2) return null;
if (typeof id !== 'number' || id < 0 || id > 0xFFFF) return null;
/* Generate header */
packet.header = protocol.codes.publish << protocol.CMD_SHIFT | dup | qos << protocol.QOS_SHIFT | retain;
/* Topic name */
packet.payload = packet.payload.concat(gen_string(topic));
/* Message ID */
if (qos > 0) packet.payload = packet.payload.concat(gen_number(id));
var buf = new Buffer([packet.header]
.concat(gen_length(packet.payload.length + payload.length))
.concat(packet.payload));
return Buffer.concat([buf, payload]);
};
/* Requires length be a number > 0 */
var gen_length = function(length) {
if(typeof length !== "number") return null;
if(length < 0) return null;
var len = [];
var digit = 0;
do {
digit = length % 128 | 0;
length = length / 128 | 0;
if (length > 0) {
digit = digit | 0x80;
}
len.push(digit);
} while (length > 0);
return len;
};
var gen_string = function(str, without_length) { /* based on code in (from http://farhadi.ir/downloads/utf8.js) */
if(arguments.length < 2) without_length = false;
if(typeof str !== "string") return null;
if(typeof without_length !== "boolean") return null;
var string = [];
var length = 0;
for(var i = 0; i < str.length; i++) {
var code = str.charCodeAt(i);
if (code < 128) {
string.push(code); ++length;
} else if (code < 2048) {
string.push(192 + ((code >> 6 ) )); ++length;
string.push(128 + ((code ) & 63)); ++length;
} else if (code < 65536) {
string.push(224 + ((code >> 12) )); ++length;
string.push(128 + ((code >> 6 ) & 63)); ++length;
string.push(128 + ((code ) & 63)); ++length;
} else if (code < 2097152) {
string.push(240 + ((code >> 18) )); ++length;
string.push(128 + ((code >> 12) & 63)); ++length;
string.push(128 + ((code >> 6 ) & 63)); ++length;
string.push(128 + ((code ) & 63)); ++length;
} else {
throw new Error("Can't encode character with code " + code);
}
}
return without_length ? string : gen_number(length).concat(string);
};
var gen_number = function(num) {
var number = [num >> 8, num & 0x00FF];
return number;
};
var randint = function() { return Math.floor(Math.random() * 0xFFFF); };
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 1 1 1 1 1 1 | var Adaptor = function(opts) { opts = opts || {}; this.subReqs = {}; this.publishRoute = opts.publishRoute; this.subscribeRoute = opts.subscribeRoute; }; module.exports = Adaptor; Adaptor.prototype.onPublish = function(client, packet) { var route = this.publishRoute; if(!route) { throw new Error('unspecified publish route.'); } var payload = packet.payload; if(payload instanceof Buffer) { payload = payload.toString('utf8'); } var req = { id: packet.messageId, route: route, body: packet }; client.emit('message', req); if(packet.qos === 1) { client.socket.puback({messageId: packet.messageId}); } }; Adaptor.prototype.onSubscribe = function(client, packet) { var route = this.subscribeRoute; if(!route) { throw new Error('unspecified subscribe route.'); } var req = { id: packet.messageId, route: route, body: { subscriptions: packet.subscriptions } }; this.subReqs[packet.messageId] = packet; client.emit('message', req); }; Adaptor.prototype.onPubAck = function(client, packet) { var req = { id: packet.messageId, route: 'connector.mqttHandler.pubAck', body: { mid: packet.messageId } }; this.subReqs[packet.messageId] = packet; client.emit('message', req); }; /** * Publish message or subscription ack. * * if packet.id exist and this.subReqs[packet.id] exist then packet is a suback. * Subscription is request/response mode. * packet.id is pass from client in packet.messageId and record in Pomelo context and attached to the subscribe response packet. * packet.body is the context that returned by subscribe next callback. * * if packet.id not exist then packet is a publish message. * * otherwise packet is a illegal packet. */ Adaptor.prototype.publish = function(client, packet) { var mid = packet.id; var subreq = this.subReqs[mid]; if(subreq) { // is suback client.socket.suback({messageId: mid, granted: packet.body}); delete this.subReqs[mid]; return; } client.socket.publish(packet.body); }; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 1 1 1 16 16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /* Protocol - protocol constants */
/* Command code => mnemonic */
module.exports.types = {
0: 'reserved',
1: 'connect',
2: 'connack',
3: 'publish',
4: 'puback',
5: 'pubrec',
6: 'pubrel',
7: 'pubcomp',
8: 'subscribe',
9: 'suback',
10: 'unsubscribe',
11: 'unsuback',
12: 'pingreq',
13: 'pingresp',
14: 'disconnect',
15: 'reserved'
};
/* Mnemonic => Command code */
module.exports.codes = {};
for(var k in module.exports.types) {
var v = module.exports.types[k];
module.exports.codes[v] = k;
}
/* Header */
module.exports.CMD_SHIFT = 4;
module.exports.CMD_MASK = 0xF0;
module.exports.DUP_MASK = 0x08;
module.exports.QOS_MASK = 0x03;
module.exports.QOS_SHIFT = 1;
module.exports.RETAIN_MASK = 0x01;
/* Length */
module.exports.LENGTH_MASK = 0x7F;
module.exports.LENGTH_FIN_MASK = 0x80;
/* Connect */
module.exports.USERNAME_MASK = 0x80;
module.exports.PASSWORD_MASK = 0x40;
module.exports.WILL_RETAIN_MASK = 0x20;
module.exports.WILL_QOS_MASK = 0x18;
module.exports.WILL_QOS_SHIFT = 3;
module.exports.WILL_FLAG_MASK = 0x04;
module.exports.CLEAN_SESSION_MASK = 0x02;
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| serial.js | 35.29% | (6 / 17) | 0% | (0 / 6) | 0% | (0 / 6) | 35.29% | (6 / 17) | |
| time.js | 40% | (6 / 15) | 0% | (0 / 2) | 0% | (0 / 4) | 40% | (6 / 15) | |
| timeout.js | 28.57% | (8 / 28) | 0% | (0 / 8) | 0% | (0 / 5) | 28.57% | (8 / 28) | |
| toobusy.js | 35.29% | (6 / 17) | 0% | (0 / 8) | 0% | (0 / 3) | 35.29% | (6 / 17) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 1 1 1 1 1 1 | /**
* Filter to keep request sequence.
*/
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var taskManager = require('../../common/manager/taskManager');
module.exports = function(timeout) {
return new Filter(timeout);
};
var Filter = function(timeout) {
this.timeout = timeout;
};
/**
* request serialization after filter
*/
Filter.prototype.before = function(msg, session, next) {
taskManager.addTask(session.id, function(task) {
session.__serialTask__ = task;
next();
}, function() {
logger.error('[serial filter] msg timeout, msg:' + JSON.stringify(msg));
}, this.timeout);
};
/**
* request serialization after filter
*/
Filter.prototype.after = function(err, msg, session, resp, next) {
var task = session.__serialTask__;
if(task) {
if(!task.done() && !err) {
err = new Error('task time out. msg:' + JSON.stringify(msg));
}
}
next(err);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | 1 1 1 1 1 1 | /**
* Filter for statistics.
* Record used time for each request.
*/
var conLogger = require('pomelo-logger').getLogger('con-log', __filename);
var utils = require('../../util/utils');
module.exports = function() {
return new Filter();
};
var Filter = function() {
};
Filter.prototype.before = function(msg, session, next) {
session.__startTime__ = Date.now();
next();
};
Filter.prototype.after = function(err, msg, session, resp, next) {
var start = session.__startTime__;
if(typeof start === 'number') {
var timeUsed = Date.now() - start;
var log = {
route : msg.__route__,
args : msg,
time : utils.format(new Date(start)),
timeUsed : timeUsed
};
conLogger.info(JSON.stringify(log));
}
next(err);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 1 1 1 1 1 1 1 1 | /**
* Filter for timeout.
* Print a warn information when request timeout.
*/
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var utils = require('../../util/utils');
var DEFAULT_TIMEOUT = 3000;
var DEFAULT_SIZE = 500;
module.exports = function(timeout, maxSize) {
return new Filter(timeout || DEFAULT_TIMEOUT, maxSize || DEFAULT_SIZE);
};
var Filter = function(timeout, maxSize) {
this.timeout = timeout;
this.maxSize = maxSize;
this.timeouts = {};
this.curId = 0;
};
Filter.prototype.before = function(msg, session, next) {
var count = utils.size(this.timeouts);
if(count > this.maxSize) {
logger.warn('timeout filter is out of range, current size is %s, max size is %s', count, this.maxSize);
next();
return;
}
this.curId++;
this.timeouts[this.curId] = setTimeout(function() {
logger.error('request %j timeout.', msg.__route__);
}, this.timeout);
session.__timeout__ = this.curId;
next();
};
Filter.prototype.after = function(err, msg, session, resp, next) {
var timeout = this.timeouts[session.__timeout__];
if(timeout) {
clearTimeout(timeout);
delete this.timeouts[session.__timeout__];
}
next(err);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 1 1 1 1 1 1 | /**
* Filter for toobusy.
* if the process is toobusy, just skip the new request
*/
var conLogger = require('pomelo-logger').getLogger('con-log', __filename);
var toobusy = null;
var DEFAULT_MAXLAG = 70;
module.exports = function(maxLag) {
return new Filter(maxLag || DEFAULT_MAXLAG);
};
var Filter = function(maxLag) {
try {
toobusy = require('toobusy');
} catch(e) {
}
if(!!toobusy) {
toobusy.maxLag(maxLag);
}
};
Filter.prototype.before = function(msg, session, next) {
if (!!toobusy && toobusy()) {
conLogger.warn('[toobusy] reject request msg: ' + msg);
var err = new Error('Server toobusy!');
err.code = 500;
next(err);
} else {
next();
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| rpcLog.js | 38.89% | (7 / 18) | 0% | (0 / 6) | 0% | (0 / 4) | 38.89% | (7 / 18) | |
| toobusy.js | 36.84% | (7 / 19) | 0% | (0 / 10) | 0% | (0 / 3) | 36.84% | (7 / 19) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 1 1 1 1 1 1 1 | /**
* Filter for rpc log.
* Record used time for remote process call.
*/
var rpcLogger = require('pomelo-logger').getLogger('rpc-log', __filename);
var utils = require('../../util/utils');
module.exports = function() {
return new Filter();
};
var Filter = function () {
};
Filter.prototype.name = 'rpcLog';
/**
* Before filter for rpc
*/
Filter.prototype.before = function(serverId, msg, opts, next) {
opts = opts||{};
opts.__start_time__ = Date.now();
next();
};
/**
* After filter for rpc
*/
Filter.prototype.after = function(serverId, msg, opts, next) {
if(!!opts && !!opts.__start_time__) {
var start = opts.__start_time__;
var end = Date.now();
var timeUsed = end - start;
var log = {
route: msg.service,
args: msg.args,
time: utils.format(new Date(start)),
timeUsed: timeUsed
};
rpcLogger.info(JSON.stringify(log));
}
next();
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 1 1 1 1 1 1 1 | /**
* Filter for rpc log.
* Reject rpc request when toobusy
*/
var rpcLogger = require('pomelo-logger').getLogger('rpc-log', __filename);
var toobusy = null;
var DEFAULT_MAXLAG = 70;
module.exports = function(maxLag) {
return new Filter(maxLag || DEFAULT_MAXLAG);
};
var Filter = function(maxLag) {
try {
toobusy = require('toobusy');
} catch(e) {
}
if(!!toobusy) {
toobusy.maxLag(maxLag);
}
};
Filter.prototype.name = 'toobusy';
/**
* Before filter for rpc
*/
Filter.prototype.before = function(serverId, msg, opts, next) {
opts = opts||{};
if (!!toobusy && toobusy()) {
rpcLogger.warn('Server too busy for rpc request, serverId:' + serverId + ' msg: ' + msg);
var err = new Error('Backend server ' + serverId + ' is too busy now!');
err.code = 500;
next(err);
} else {
next();
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| master.js | 16.05% | (13 / 81) | 0% | (0 / 32) | 0% | (0 / 16) | 16.05% | (13 / 81) | |
| starter.js | 15.04% | (17 / 113) | 0% | (0 / 47) | 0% | (0 / 10) | 15.04% | (17 / 113) | |
| watchdog.js | 21.95% | (18 / 82) | 0% | (0 / 24) | 0% | (0 / 15) | 21.95% | (18 / 82) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 1 1 1 1 1 1 1 1 1 1 1 1 1 | var starter = require('./starter');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var crashLogger = require('pomelo-logger').getLogger('crash-log', __filename);
var adminLogger = require('pomelo-logger').getLogger('admin-log', __filename);
var admin = require('pomelo-admin');
var util = require('util');
var utils = require('../util/utils');
var moduleUtil = require('../util/moduleUtil');
var Constants = require('../util/constants');
var Server = function(app, opts) {
this.app = app;
this.masterInfo = app.getMaster();
this.registered = {};
this.modules = [];
opts = opts || {};
opts.port = this.masterInfo.port;
opts.env = this.app.get(Constants.RESERVED.ENV);
this.closeWatcher = opts.closeWatcher;
this.masterConsole = admin.createMasterConsole(opts);
};
module.exports = Server;
Server.prototype.start = function(cb) {
moduleUtil.registerDefaultModules(true, this.app, this.closeWatcher);
moduleUtil.loadModules(this, this.masterConsole);
var self = this;
// start master console
this.masterConsole.start(function(err) {
if(err) {
process.exit(0);
}
moduleUtil.startModules(self.modules, function(err) {
if(err) {
utils.invokeCallback(cb, err);
return;
}
if(self.app.get(Constants.RESERVED.MODE) !== Constants.RESERVED.STAND_ALONE) {
starter.runServers(self.app);
}
utils.invokeCallback(cb);
});
});
this.masterConsole.on('error', function(err) {
if(!!err) {
logger.error('masterConsole encounters with error: ' + err.stack);
return;
}
});
this.masterConsole.on('reconnect', function(info){
self.app.addServers([info]);
});
// monitor servers disconnect event
this.masterConsole.on('disconnect', function(id, type, info, reason) {
crashLogger.info(util.format('[%s],[%s],[%s],[%s]', type, id, Date.now(), reason || 'disconnect'));
var count = 0;
var time = 0;
var pingTimer = null;
var server = self.app.getServerById(id);
var stopFlags = self.app.get(Constants.RESERVED.STOP_SERVERS) || [];
if(!!server && (server[Constants.RESERVED.AUTO_RESTART] === 'true' || server[Constants.RESERVED.RESTART_FORCE] === 'true') && stopFlags.indexOf(id) < 0) {
var setTimer = function(time) {
pingTimer = setTimeout(function() {
utils.ping(server.host, function(flag) {
if(flag) {
handle();
} else {
count++;
if(count > 3) {
time = Constants.TIME.TIME_WAIT_MAX_PING;
} else {
time = Constants.TIME.TIME_WAIT_PING * count;
}
setTimer(time);
}
});
}, time);
};
setTimer(time);
var handle = function() {
clearTimeout(pingTimer);
utils.checkPort(server, function(status) {
if(status === 'error') {
utils.invokeCallback(cb, new Error('Check port command executed with error.'));
return;
} else if(status === 'busy') {
if(!!server[Constants.RESERVED.RESTART_FORCE]) {
starter.kill([info.pid], [server]);
} else {
utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.'));
return;
}
}
setTimeout(function() {
starter.run(self.app, server, null);
}, Constants.TIME.TIME_WAIT_STOP);
});
};
}
});
// monitor servers register event
this.masterConsole.on('register', function(record) {
starter.bindCpu(record.id, record.pid, record.host);
});
this.masterConsole.on('admin-log', function(log, error) {
if(error) {
adminLogger.error(JSON.stringify(log));
} else {
adminLogger.info(JSON.stringify(log));
}
});
};
Server.prototype.stop = function(cb) {
this.masterConsole.stop();
process.nextTick(cb);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var cp = require('child_process');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var starter = module.exports;
var util = require('util');
var utils = require('../util/utils');
var Constants = require('../util/constants');
var env = Constants.RESERVED.ENV_DEV;
var os=require('os');
var cpus = {};
var pomelo = require('../pomelo');
/**
* Run all servers
*
* @param {Object} app current application context
* @return {Void}
*/
starter.runServers = function(app) {
var server, servers;
var condition = app.startId || app.type;
switch(condition) {
case Constants.RESERVED.MASTER:
break;
case Constants.RESERVED.ALL:
servers = app.getServersFromConfig();
for (var serverId in servers) {
this.run(app, servers[serverId]);
}
break;
default:
server = app.getServerFromConfig(condition);
if(!!server) {
this.run(app, server);
} else {
servers = app.get(Constants.RESERVED.SERVERS)[condition];
for(var i=0; i<servers.length; i++) {
this.run(app, servers[i]);
}
}
}
};
/**
* Run server
*
* @param {Object} app current application context
* @param {Object} server
* @return {Void}
*/
starter.run = function(app, server, cb) {
env = app.get(Constants.RESERVED.ENV);
var cmd, key;
if (utils.isLocal(server.host)) {
var options = [];
if (!!server.args) {
if(typeof server.args === 'string') {
options.push(server.args.trim());
} else {
options = options.concat(server.args);
}
}
cmd = app.get(Constants.RESERVED.MAIN);
options.push(cmd);
options.push(util.format('env=%s', env));
for(key in server) {
if(key === Constants.RESERVED.CPU) {
cpus[server.id] = server[key];
}
options.push(util.format('%s=%s', key, server[key]));
}
starter.localrun(process.execPath, null, options, cb);
} else {
cmd = util.format('cd "%s" && "%s"', app.getBase(), process.execPath);
var arg = server.args;
if (arg !== undefined) {
cmd += arg;
}
cmd += util.format(' "%s" env=%s ', app.get(Constants.RESERVED.MAIN), env);
for(key in server) {
if(key === Constants.RESERVED.CPU) {
cpus[server.id] = server[key];
}
cmd += util.format(' %s=%s ', key, server[key]);
}
starter.sshrun(cmd, server.host, cb);
}
};
/**
* Bind process with cpu
*
* @param {String} sid server id
* @param {String} pid process id
* @param {String} host server host
* @return {Void}
*/
starter.bindCpu = function(sid, pid, host) {
if(os.platform() === Constants.PLATFORM.LINUX && cpus[sid] !== undefined) {
if (utils.isLocal(host)) {
var options = [];
options.push('-pc');
options.push(cpus[sid]);
options.push(pid);
starter.localrun(Constants.COMMAND.TASKSET, null, options);
}
else {
var cmd = util.format('taskset -pc "%s" "%s"', cpus[sid], pid);
starter.sshrun(cmd, host, null);
}
}
};
/**
* Kill application in all servers
*
* @param {String} pids array of server's pid
* @param {String} serverIds array of serverId
*/
starter.kill = function(pids, servers) {
var cmd;
for(var i = 0; i < servers.length; i++) {
var server = servers[i];
if(utils.isLocal(server.host)) {
var options = [];
if(os.platform() === Constants.PLATFORM.WIN) {
cmd = Constants.COMMAND.TASKKILL;
options.push('/pid');
options.push('/f');
} else {
cmd = Constants.COMMAND.KILL;
options.push(-9);
}
options.push(pids[i]);
starter.localrun(cmd,null,options);
} else {
if(os.platform() === Constants.PLATFORM.WIN) {
cmd = util.format('taskkill /pid %s /f', pids[i]);
} else {
cmd = util.format('kill -9 %s', pids[i]);
}
starter.sshrun(cmd, server.host);
}
}
};
/**
* Use ssh to run command.
*
* @param {String} cmd command that would be executed in the remote server
* @param {String} host remote server host
* @param {Function} cb callback function
*
*/
starter.sshrun = function(cmd, host, cb) {
var args = [];
args.push(host);
var ssh_params = pomelo.app.get(Constants.RESERVED.SSH_CONFIG_PARAMS);
if(!!ssh_params && Array.isArray(ssh_params)) {
args = args.concat(ssh_params);
}
args.push(cmd);
logger.info('Executing ' + cmd + ' on ' + host + ':22');
spawnProcess(Constants.COMMAND.SSH, host, args, cb);
return;
};
/**
* Run local command.
*
* @param {String} cmd
* @param {Callback} callback
*
*/
starter.localrun = function (cmd, host, options, callback) {
logger.info('Executing ' + cmd + ' ' + options + ' locally');
spawnProcess(cmd, host, options, callback);
};
/**
* Fork child process to run command.
*
* @param {String} command
* @param {Object} options
* @param {Callback} callback
*
*/
var spawnProcess = function(command, host, options, cb) {
var child = null;
if(env === Constants.RESERVED.ENV_DEV) {
child = cp.spawn(command, options);
var prefix = command === Constants.COMMAND.SSH ? '[' + host + '] ' : '';
child.stderr.on('data', function (chunk) {
var msg = chunk.toString();
process.stderr.write(msg);
if(!!cb) {
cb(msg);
}
});
child.stdout.on('data', function (chunk) {
var msg = prefix + chunk.toString();
process.stdout.write(msg);
});
} else {
child = cp.spawn(command, options, {detached: true, stdio: 'inherit'});
child.unref();
}
child.on('exit', function (code) {
if(code !== 0) {
logger.warn('child process exit with error, error code: %s, executed command: %s', code, command);
}
if (typeof cb === 'function') {
cb(code === 0 ? null : code);
}
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var utils = require('../util/utils');
var Constants = require('../util/constants');
var countDownLatch = require('../util/countDownLatch');
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var Watchdog = function(app, service) {
EventEmitter.call(this);
this.app = app;
this.service = service;
this.isStarted = false;
this.count = utils.size(app.getServersFromConfig());
this.servers = {};
this.listeners = {};
};
util.inherits(Watchdog, EventEmitter);
module.exports = Watchdog;
Watchdog.prototype.addServer = function(server) {
if(!server) {
return;
}
this.servers[server.id] = server;
this.notify({action: 'addServer', server: server});
};
Watchdog.prototype.removeServer = function(id) {
if(!id) {
return;
}
this.unsubscribe(id);
delete this.servers[id];
this.notify({action: 'removeServer', id: id});
};
Watchdog.prototype.reconnectServer = function(server) {
var self = this;
if(!server) {
return;
}
if(!this.servers[server.id]) {
this.servers[server.id] = server;
}
//replace server in reconnect server
this.notifyById(server.id, {action: 'replaceServer', servers: self.servers});
// notify other server to add server
this.notify({action: 'addServer', server: server});
// add server in listener
this.subscribe(server.id);
};
Watchdog.prototype.subscribe = function(id) {
this.listeners[id] = 1;
};
Watchdog.prototype.unsubscribe = function(id) {
delete this.listeners[id];
};
Watchdog.prototype.query = function() {
return this.servers;
};
Watchdog.prototype.record = function(id) {
if(!this.isStarted && --this.count < 0) {
var usedTime = Date.now() - this.app.startTime;
logger.info('all servers startup in %s ms', usedTime);
this.notify({action: 'startOver'});
this.isStarted = true;
}
};
Watchdog.prototype.notifyById = function(id, msg) {
this.service.agent.request(id, Constants.KEYWORDS.MONITOR_WATCHER, msg, function(signal) {
if(signal !== Constants.SIGNAL.OK) {
logger.error('master watchdog fail to notify to monitor, id: %s, msg: %j', id, msg);
} else {
logger.debug('master watchdog notify to monitor success, id: %s, msg: %j', id, msg);
}
});
};
Watchdog.prototype.notify = function(msg) {
var listeners = this.listeners;
var success = true;
var fails = [];
var timeouts = [];
var requests = {};
var count = utils.size(listeners);
if(count === 0) {
logger.warn('master watchdog listeners is none, msg: %j', msg);
return;
}
var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function(isTimeout) {
if(!!isTimeout) {
for(var key in requests) {
if(!requests[key]) {
timeouts.push(key);
}
}
logger.error('master watchdog request timeout message: %j, timeouts: %j, fails: %j', msg, timeouts, fails);
}
if(!success) {
logger.error('master watchdog request fail message: %j, fails: %j', msg, fails);
}
});
var moduleRequest = function(self, id) {
return (function() {
self.service.agent.request(id, Constants.KEYWORDS.MONITOR_WATCHER, msg, function(signal) {
if(signal !== Constants.SIGNAL.OK) {
fails.push(id);
success = false;
}
requests[id] = 1;
latch.done();
});
})();
};
for(var id in listeners) {
requests[id] = 0;
moduleRequest(this, id);
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| console.js | 11.31% | (32 / 283) | 0% | (0 / 96) | 0% | (0 / 48) | 11.31% | (32 / 283) | |
| masterwatcher.js | 26.56% | (17 / 64) | 0% | (0 / 24) | 0% | (0 / 11) | 26.56% | (17 / 64) | |
| monitorwatcher.js | 25.64% | (20 / 78) | 0% | (0 / 30) | 0% | (0 / 14) | 25.64% | (20 / 78) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*!
* Pomelo -- consoleModule serverStop stop/kill
* Copyright(c) 2012 fantasyni <fantasyni@163.com>
* MIT Licensed
*/
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var countDownLatch = require('../util/countDownLatch');
var utils = require('../util/utils');
var Constants = require('../util/constants');
var starter = require('../master/starter');
var exec = require('child_process').exec;
module.exports = function(opts) {
return new Module(opts);
};
module.exports.moduleId = '__console__';
var Module = function(opts) {
opts = opts || {};
this.app = opts.app;
this.starter = opts.starter;
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
var serverId = agent.id;
switch(msg.signal) {
case 'stop':
if(agent.type === Constants.RESERVED.MASTER) {
return;
}
this.app.stop(true);
break;
case 'list':
var serverType = agent.type;
var pid = process.pid;
var heapUsed = (process.memoryUsage().heapUsed/(1024 * 1024)).toFixed(2);
var rss = (process.memoryUsage().rss/(1024 * 1024)).toFixed(2);
var heapTotal = (process.memoryUsage().heapTotal/(1024 * 1024)).toFixed(2);
var uptime = (process.uptime()/60).toFixed(2);
utils.invokeCallback(cb, {
serverId: serverId,
body: {serverId:serverId, serverType: serverType, pid:pid, rss: rss, heapTotal: heapTotal, heapUsed:heapUsed, uptime:uptime}
});
break;
case 'kill':
utils.invokeCallback(cb, serverId);
if (agent.type !== 'master') {
setTimeout(function() {
process.exit(-1);
}, Constants.TIME.TIME_WAIT_MONITOR_KILL);
}
break;
case 'addCron':
this.app.addCrons([msg.cron]);
break;
case 'removeCron':
this.app.removeCrons([msg.cron]);
break;
case 'blacklist':
if(this.app.isFrontend()) {
var connector = this.app.components.__connector__;
connector.blacklist = connector.blacklist.concat(msg.blacklist);
}
break;
case 'restart':
if(agent.type === Constants.RESERVED.MASTER) {
return;
}
var self = this;
var server = this.app.get(Constants.RESERVED.CURRENT_SERVER);
utils.invokeCallback(cb, server);
process.nextTick(function() {
self.app.stop(true);
});
break;
default:
logger.error('receive error signal: %j', msg);
break;
}
};
Module.prototype.clientHandler = function(agent, msg, cb) {
var app = this.app;
switch(msg.signal) {
case 'kill':
kill(app, agent, msg, cb);
break;
case 'stop':
stop(app, agent, msg, cb);
break;
case 'list':
list(agent, msg, cb);
break;
case 'add':
add(app, msg, cb);
break;
case 'addCron':
addCron(app, agent, msg, cb);
break;
case 'removeCron':
removeCron(app, agent, msg, cb);
break;
case 'blacklist':
blacklist(agent, msg, cb);
break;
case 'restart':
restart(app, agent, msg, cb);
break;
default:
utils.invokeCallback(cb, new Error('The command cannot be recognized, please check.'), null);
break;
}
};
var kill = function(app, agent, msg, cb) {
var sid, record;
var serverIds = [];
var count = utils.size(agent.idMap);
var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_MASTER_KILL}, function(isTimeout) {
if (!isTimeout) {
utils.invokeCallback(cb, null, {code: 'ok'});
} else {
utils.invokeCallback(cb, null, {code: 'remained', serverIds: serverIds});
}
setTimeout(function() {
process.exit(-1);
}, Constants.TIME.TIME_WAIT_MONITOR_KILL);
});
var agentRequestCallback = function(msg) {
for (var i = 0; i < serverIds.length; ++i) {
if (serverIds[i] === msg) {
serverIds.splice(i,1);
latch.done();
break;
}
}
};
for(sid in agent.idMap) {
record = agent.idMap[sid];
serverIds.push(record.id);
agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, agentRequestCallback);
}
};
var stop = function(app, agent, msg, cb) {
var serverIds = msg.ids;
if(!!serverIds.length) {
var servers = app.getServers();
app.set(Constants.RESERVED.STOP_SERVERS, serverIds);
for(var i=0; i<serverIds.length; i++) {
var serverId = serverIds[i];
if(!servers[serverId]) {
utils.invokeCallback(cb, new Error('Cannot find the server to stop.'), null);
} else {
agent.notifyById(serverId, module.exports.moduleId, { signal: msg.signal });
}
}
utils.invokeCallback(cb, null, { status: "part" });
} else {
var servers = app.getServers();
var serverIds = [];
for(var i in servers){
serverIds.push(i)
}
app.set(Constants.RESERVED.STOP_SERVERS, serverIds);
agent.notifyAll(module.exports.moduleId, { signal: msg.signal });
setTimeout(function() {
app.stop(true);
utils.invokeCallback(cb, null, { status: "all" });
}, Constants.TIME.TIME_WAIT_STOP);
}
};
var restart = function(app, agent, msg, cb) {
var successFlag;
var successIds = [];
var serverIds = msg.ids;
var type = msg.type;
var servers;
if(!serverIds.length && !!type) {
servers = app.getServersByType(type);
if(!servers) {
utils.invokeCallback(cb, new Error('restart servers with unknown server type: ' + type));
return;
}
for(var i=0; i<servers.length; i++) {
serverIds.push(servers[i].id);
}
} else if(!serverIds.length) {
servers = app.getServers();
for(var key in servers) {
serverIds.push(key);
}
}
var count = serverIds.length;
var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {
if(!successFlag) {
utils.invokeCallback(cb, new Error('all servers start failed.'));
return;
}
utils.invokeCallback(cb, null, utils.arrayDiff(serverIds, successIds));
});
var request = function(id) {
return (function() {
agent.request(id, module.exports.moduleId, { signal: msg.signal }, function(msg) {
if(!utils.size(msg)) {
latch.done();
return;
}
setTimeout(function() {
runServer(app, msg, function(err, status) {
if(!!err) {
logger.error('restart ' + id + ' failed.');
} else {
successIds.push(id);
successFlag = true;
}
latch.done();
});
}, Constants.TIME.TIME_WAIT_RESTART);
});
})();
};
for(var j=0; j<serverIds.length; j++) {
request(serverIds[j]);
}
};
var list = function(agent, msg, cb) {
var sid, record;
var serverInfo = {};
var count = utils.size(agent.idMap);
var latch = countDownLatch.createCountDownLatch(count, {timeout: Constants.TIME.TIME_WAIT_COUNTDOWN}, function() {
utils.invokeCallback(cb, null, { msg: serverInfo });
});
var callback = function(msg) {
serverInfo[msg.serverId] = msg.body;
latch.done();
};
for(sid in agent.idMap) {
record = agent.idMap[sid];
agent.request(record.id, module.exports.moduleId, { signal: msg.signal }, callback);
}
};
var add = function(app, msg, cb) {
if(checkCluster(msg)) {
startCluster(app, msg, cb);
} else {
startServer(app, msg, cb);
}
reset(ServerInfo);
};
var addCron = function(app, agent, msg, cb) {
var cron = parseArgs(msg, CronInfo, cb);
sendCronInfo(cron, agent, msg, CronInfo, cb);
};
var removeCron = function(app, agent, msg, cb) {
var cron = parseArgs(msg, RemoveCron, cb);
sendCronInfo(cron, agent, msg, RemoveCron, cb);
};
var blacklist = function(agent, msg, cb) {
var ips = msg.args;
for(var i=0; i<ips.length; i++) {
if(!(new RegExp(/(\d+)\.(\d+)\.(\d+)\.(\d+)/g).test(ips[i]))) {
utils.invokeCallback(cb, new Error('blacklist ip: ' + ips[i] + ' is error format.'), null);
return;
}
}
agent.notifyAll(module.exports.moduleId, { signal: msg.signal, blacklist: msg.args });
process.nextTick(function() {
cb(null, { status: "ok" });
});
};
var checkPort = function(server, cb) {
if (!server.port && !server.clientPort) {
utils.invokeCallback(cb, 'leisure');
return;
}
var p = server.port || server.clientPort;
var host = server.host;
var cmd = 'netstat -tln | grep ';
if (!utils.isLocal(host)) {
cmd = 'ssh ' + host + ' ' + cmd;
}
exec(cmd + p, function(err, stdout, stderr) {
if (stdout || stderr) {
utils.invokeCallback(cb, 'busy');
} else {
p = server.clientPort;
exec(cmd + p, function(err, stdout, stderr) {
if (stdout || stderr) {
utils.invokeCallback(cb, 'busy');
} else {
utils.invokeCallback(cb, 'leisure');
}
});
}
});
};
var parseArgs = function(msg, info, cb) {
var rs = {};
var args = msg.args;
for(var i =0; i<args.length; i++) {
if(args[i].indexOf('=') < 0) {
cb(new Error('Error server parameters format.'), null);
return;
}
var pairs = args[i].split('=');
var key = pairs[0];
if(!!info[key]) {
info[key] = 1;
}
rs[pairs[0]] = pairs[1];
}
return rs;
};
var sendCronInfo = function(cron, agent, msg, info, cb) {
if(isReady(info) && (cron.serverId || cron.serverType)) {
if(!!cron.serverId) {
agent.notifyById(cron.serverId, module.exports.moduleId, { signal: msg.signal, cron: cron });
} else {
agent.notifyByType(cron.serverType, module.exports.moduleId, { signal: msg.signal, cron: cron });
}
process.nextTick(function() {
cb(null, { status: "ok" });
});
} else {
cb(new Error('Miss necessary server parameters.'), null);
}
reset(info);
};
var startServer = function(app, msg, cb) {
var server = parseArgs(msg, ServerInfo, cb);
if(isReady(ServerInfo)) {
runServer(app, server, cb);
} else {
cb(new Error('Miss necessary server parameters.'), null);
}
};
var runServer = function(app, server, cb) {
checkPort(server, function(status) {
if(status === 'busy') {
utils.invokeCallback(cb, new Error('Port occupied already, check your server to add.'));
} else {
starter.run(app, server, function(err) {
if(err) {
utils.invokeCallback(cb, new Error(err), null);
return;
}
});
process.nextTick(function() {
utils.invokeCallback(cb, null, { status: "ok" });
});
}
});
};
var startCluster = function(app, msg, cb) {
var serverMap = {};
var fails = [];
var successFlag;
var serverInfo = parseArgs(msg, ClusterInfo, cb);
utils.loadCluster(app, serverInfo, serverMap);
var count = utils.size(serverMap);
var latch = countDownLatch.createCountDownLatch(count, function() {
if(!successFlag) {
utils.invokeCallback(cb, new Error('all servers start failed.'));
return;
}
utils.invokeCallback(cb, null, fails);
});
var start = function(server) {
return (function() {
checkPort(server, function(status) {
if(status === 'busy') {
fails.push(server);
latch.done();
} else {
starter.run(app, server, function(err) {
if(err) {
fails.push(server);
latch.done();
}
});
process.nextTick(function() {
successFlag = true;
latch.done();
});
}
});
})();
};
for(var key in serverMap) {
var server = serverMap[key];
start(server);
}
};
var checkCluster = function(msg) {
var flag = false;
var args = msg.args;
for(var i=0; i < args.length; i++) {
if(utils.startsWith(args[i], Constants.RESERVED.CLUSTER_COUNT)) {
flag = true;
}
}
return flag;
};
var isReady = function(info) {
for(var key in info) {
if(info[key]) {
return false;
}
}
return true;
};
var reset = function(info) {
for(var key in info) {
info[key] = 0;
}
};
var ServerInfo = {
host: 0,
port: 0,
id: 0,
serverType: 0
};
var CronInfo = {
id: 0,
action: 0,
time: 0
};
var RemoveCron = {
id: 0
};
var ClusterInfo = {
host: 0,
port: 0,
clusterCount: 0
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var utils = require('../util/utils');
var Constants = require('../util/constants');
var MasterWatchdog = require('../master/watchdog');
module.exports = function(opts, consoleService) {
return new Module(opts, consoleService);
};
module.exports.moduleId = Constants.KEYWORDS.MASTER_WATCHER;
var Module = function(opts, consoleService) {
this.app = opts.app;
this.service = consoleService;
this.id = this.app.getServerId();
this.watchdog = new MasterWatchdog(this.app, this.service);
this.service.on('register', onServerAdd.bind(null, this));
this.service.on('disconnect', onServerLeave.bind(null, this));
this.service.on('reconnect', onServerReconnect.bind(null, this));
};
// ----------------- bind methods -------------------------
var onServerAdd = function(module, record) {
logger.debug('masterwatcher receive add server event, with server: %j', record);
if(!record || record.type === 'client' || !record.serverType) {
return;
}
module.watchdog.addServer(record);
};
var onServerReconnect = function(module, record) {
logger.debug('masterwatcher receive reconnect server event, with server: %j', record);
if(!record || record.type === 'client' || !record.serverType) {
logger.warn('onServerReconnect receive wrong message: %j', record);
return;
}
module.watchdog.reconnectServer(record);
};
var onServerLeave = function(module, id, type) {
logger.debug('masterwatcher receive remove server event, with server: %s, type: %s', id, type);
if(!id) {
logger.warn('onServerLeave receive server id is empty.');
return;
}
if(type !== 'client') {
module.watchdog.removeServer(id);
}
};
// ----------------- module methods -------------------------
Module.prototype.start = function(cb) {
utils.invokeCallback(cb);
};
Module.prototype.masterHandler = function(agent, msg, cb) {
if(!msg) {
logger.warn('masterwatcher receive empty message.');
return;
}
var func = masterMethods[msg.action];
if(!func) {
logger.info('masterwatcher unknown action: %j', msg.action);
return;
}
func(this, agent, msg, cb);
};
// ----------------- monitor request methods -------------------------
var subscribe = function(module, agent, msg, cb) {
if(!msg) {
utils.invokeCallback(cb, new Error('masterwatcher subscribe empty message.'));
return;
}
module.watchdog.subscribe(msg.id);
utils.invokeCallback(cb, null, module.watchdog.query());
};
var unsubscribe = function(module, agent, msg, cb) {
if(!msg) {
utils.invokeCallback(cb, new Error('masterwatcher unsubscribe empty message.'));
return;
}
module.watchdog.unsubscribe(msg.id);
utils.invokeCallback(cb);
};
var query = function(module, agent, msg, cb) {
utils.invokeCallback(cb, null, module.watchdog.query());
};
var record = function(module, agent, msg) {
if(!msg) {
utils.invokeCallback(cb, new Error('masterwatcher record empty message.'));
return;
}
module.watchdog.record(msg.id);
};
var masterMethods = {
'subscribe': subscribe,
'unsubscribe': unsubscribe,
'query': query,
'record': record
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var utils = require('../util/utils');
var events = require('../util/events');
var Constants = require('../util/constants');
var util = require('util');
module.exports = function(opts, consoleService) {
return new Module(opts, consoleService);
};
module.exports.moduleId = Constants.KEYWORDS.MONITOR_WATCHER;
var Module = function(opts, consoleService) {
this.app = opts.app;
this.service = consoleService;
this.id = this.app.getServerId();
this.app.event.on(events.START_SERVER, finishStart.bind(null, this));
};
Module.prototype.start = function(cb) {
subscribeRequest(this, this.service.agent, this.id, cb);
};
Module.prototype.monitorHandler = function(agent, msg, cb) {
if(!msg || !msg.action) {
return;
}
var func = monitorMethods[msg.action];
if(!func) {
logger.info('monitorwatcher unknown action: %j', msg.action);
return;
}
func(this, agent, msg, cb);
};
// ----------------- monitor start method -------------------------
var subscribeRequest = function(self, agent, id, cb) {
var msg = {action: 'subscribe', id: id};
agent.request(Constants.KEYWORDS.MASTER_WATCHER, msg, function(err, servers) {
if(err) {
logger.error('subscribeRequest request to master with error: %j', err.stack);
utils.invokeCallback(cb, err);
}
var res = [];
for(var id in servers) {
res.push(servers[id]);
}
addServers(self, res);
utils.invokeCallback(cb);
});
};
// ----------------- monitor request methods -------------------------
var addServer = function(self, agent, msg, cb) {
logger.debug('[%s] receive addServer signal: %j', self.app.serverId, msg);
if(!msg || !msg.server) {
logger.warn('monitorwatcher addServer receive empty message: %j', msg);
utils.invokeCallback(cb, Constants.SIGNAL.FAIL);
return;
}
addServers(self, [msg.server]);
utils.invokeCallback(cb, Constants.SIGNAL.OK);
};
var removeServer = function(self, agent, msg, cb) {
logger.debug('%s receive removeServer signal: %j', self.app.serverId, msg);
if(!msg || !msg.id) {
logger.warn('monitorwatcher removeServer receive empty message: %j', msg);
utils.invokeCallback(cb, Constants.SIGNAL.FAIL);
return;
}
removeServers(self, [msg.id]);
utils.invokeCallback(cb, Constants.SIGNAL.OK);
};
var replaceServer = function(self, agent, msg, cb) {
logger.debug('%s receive replaceServer signal: %j', self.app.serverId, msg);
if(!msg || !msg.servers) {
logger.warn('monitorwatcher replaceServer receive empty message: %j', msg);
utils.invokeCallback(cb, Constants.SIGNAL.FAIL);
return;
}
replaceServers(self, msg.servers);
utils.invokeCallback(cb, Constants.SIGNAL.OK);
};
var startOver = function(self, agent, msg, cb) {
var fun = self.app.lifecycleCbs[Constants.LIFECYCLE.AFTER_STARTALL];
if(!!fun) {
fun.call(null, self.app);
}
self.app.event.emit(events.START_ALL);
utils.invokeCallback(cb, Constants.SIGNAL.OK);
};
// ----------------- common methods -------------------------
var addServers = function(self, servers) {
if(!servers || !servers.length) {
return;
}
self.app.addServers(servers);
};
var removeServers = function(self, ids) {
if(!ids || !ids.length) {
return;
}
self.app.removeServers(ids);
};
var replaceServers = function(self, servers) {
self.app.replaceServers(servers);
};
// ----------------- bind methods -------------------------
var finishStart = function(self, id) {
var msg = {action: 'record', id: id};
self.service.agent.notify(Constants.KEYWORDS.MASTER_WATCHER, msg);
};
var monitorMethods = {
'addServer': addServer,
'removeServer': removeServer,
'replaceServer': replaceServer,
'startOver': startOver
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| monitor.js | 26.19% | (11 / 42) | 0% | (0 / 6) | 0% | (0 / 11) | 26.19% | (11 / 42) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 1 1 1 1 1 1 1 1 1 1 1 | /**
* Component for monitor.
* Load and start monitor client.
*/
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var admin = require('pomelo-admin');
var moduleUtil = require('../util/moduleUtil');
var utils = require('../util/utils');
var Constants = require('../util/constants');
var Monitor = function(app, opts) {
opts = opts || {};
this.app = app;
this.serverInfo = app.getCurServer();
this.masterInfo = app.getMaster();
this.modules = [];
this.closeWatcher = opts.closeWatcher;
this.monitorConsole = admin.createMonitorConsole({
id: this.serverInfo.id,
type: this.app.getServerType(),
host: this.masterInfo.host,
port: this.masterInfo.port,
info: this.serverInfo,
env: this.app.get(Constants.RESERVED.ENV),
authServer: app.get('adminAuthServerMonitor') // auth server function
});
};
module.exports = Monitor;
Monitor.prototype.start = function(cb) {
moduleUtil.registerDefaultModules(false, this.app, this.closeWatcher);
this.startConsole(cb);
};
Monitor.prototype.startConsole = function(cb) {
moduleUtil.loadModules(this, this.monitorConsole);
var self = this;
this.monitorConsole.start(function(err) {
if (err) {
utils.invokeCallback(cb, err);
return;
}
moduleUtil.startModules(self.modules, function(err) {
utils.invokeCallback(cb, err);
return;
});
});
this.monitorConsole.on('error', function(err) {
if(!!err) {
logger.error('monitorConsole encounters with error: %j', err.stack);
return;
}
});
};
Monitor.prototype.stop = function(cb) {
this.monitorConsole.stop();
this.modules = [];
process.nextTick(function() {
utils.invokeCallback(cb);
});
};
// monitor reconnect to master
Monitor.prototype.reconnect = function(masterInfo) {
var self = this;
this.stop(function() {
self.monitorConsole = admin.createMonitorConsole({
id: self.serverInfo.id,
type: self.app.getServerType(),
host: masterInfo.host,
port: masterInfo.port,
info: self.serverInfo,
env: self.app.get(Constants.RESERVED.ENV)
});
self.startConsole(function() {
logger.info('restart modules for server : %j finish.', self.app.serverId);
});
});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 1 1 1 1 1 1 1 1 1 1 1 1 | var utils = require('../util/utils');
var DEFAULT_FLUSH_INTERVAL = 20;
var Service = function(app, opts) {
if (!(this instanceof Service)) {
return new Service(app, opts);
}
opts = opts || {};
this.app = app;
this.flushInterval = opts.flushInterval || DEFAULT_FLUSH_INTERVAL;
this.sessions = {}; // sid -> msg queue
this.tid = null;
};
module.exports = Service;
Service.prototype.start = function(cb) {
this.tid = setInterval(flush.bind(null, this), this.flushInterval);
process.nextTick(function() {
utils.invokeCallback(cb);
});
};
Service.prototype.stop = function(force, cb) {
if(this.tid) {
clearInterval(this.tid);
this.tid = null;
}
process.nextTick(function() {
utils.invokeCallback(cb);
});
};
Service.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {
opts = opts || {};
if(opts.type === 'broadcast') {
doBroadcast(this, msg, opts.userOptions);
} else {
doBatchPush(this, msg, recvs);
}
process.nextTick(function() {
utils.invokeCallback(cb);
});
};
var doBroadcast = function(self, msg, opts) {
var channelService = self.app.get('channelService');
var sessionService = self.app.get('sessionService');
if(opts.binded) {
sessionService.forEachBindedSession(function(session) {
if(channelService.broadcastFilter &&
!channelService.broadcastFilter(session, msg, opts.filterParam)) {
return;
}
enqueue(self, session, msg);
});
} else {
sessionService.forEachSession(function(session) {
if(channelService.broadcastFilter &&
!channelService.broadcastFilter(session, msg, opts.filterParam)) {
return;
}
enqueue(self, session, msg);
});
}
};
var doBatchPush = function(self, msg, recvs) {
var sessionService = self.app.get('sessionService');
var session;
for(var i=0, l=recvs.length; i<l; i++) {
session = sessionService.get(recvs[i]);
if(session) {
enqueue(self, session, msg);
}
}
};
var enqueue = function(self, session, msg) {
var queue = self.sessions[session.id];
if(!queue) {
queue = self.sessions[session.id] = [];
session.once('closed', onClose.bind(null, self));
}
queue.push(msg);
};
var onClose = function(self, session) {
delete self.sessions[session.id];
};
var flush = function(self) {
var sessionService = self.app.get('sessionService');
var queue, session;
for(var sid in self.sessions) {
session = sessionService.get(sid);
if(!session) {
continue;
}
queue = self.sessions[sid];
if(!queue || queue.length === 0) {
continue;
}
session.sendBatch(queue);
self.sessions[sid] = [];
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | 1 1 1 1 1 1 | var utils = require('../util/utils');
var Service = function(app, opts) {
if (!(this instanceof Service)) {
return new Service(app, opts);
}
opts = opts || {};
this.app = app;
};
module.exports = Service;
Service.prototype.schedule = function(reqId, route, msg, recvs, opts, cb) {
opts = opts || {};
if(opts.type === 'broadcast') {
doBroadcast(this, msg, opts.userOptions);
} else {
doBatchPush(this, msg, recvs);
}
if(cb) {
process.nextTick(function() {
utils.invokeCallback(cb);
});
}
};
var doBroadcast = function(self, msg, opts) {
var channelService = self.app.get('channelService');
var sessionService = self.app.get('sessionService');
if(opts.binded) {
sessionService.forEachBindedSession(function(session) {
if(channelService.broadcastFilter &&
!channelService.broadcastFilter(session, msg, opts.filterParam)) {
return;
}
sessionService.sendMessageByUid(session.uid, msg);
});
} else {
sessionService.forEachSession(function(session) {
if(channelService.broadcastFilter &&
!channelService.broadcastFilter(session, msg, opts.filterParam)) {
return;
}
sessionService.sendMessage(session.id, msg);
});
}
};
var doBatchPush = function(self, msg, recvs) {
var sessionService = self.app.get('sessionService');
for(var i=0, l=recvs.length; i<l; i++) {
sessionService.sendMessage(recvs[i], msg);
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| server.js | 17.19% | (38 / 221) | 0% | (0 / 81) | 0% | (0 / 34) | 17.19% | (38 / 221) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /**
* Implementation of server component.
* Init and start server instance.
*/
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var fs = require('fs');
var path = require('path');
var pathUtil = require('../util/pathUtil');
var Loader = require('pomelo-loader');
var utils = require('../util/utils');
var schedule = require('pomelo-scheduler');
var events = require('../util/events');
var Constants = require('../util/constants');
var FilterService = require('../common/service/filterService');
var HandlerService = require('../common/service/handlerService');
var ST_INITED = 0; // server inited
var ST_STARTED = 1; // server started
var ST_STOPED = 2; // server stoped
/**
* Server factory function.
*
* @param {Object} app current application context
* @return {Object} erver instance
*/
module.exports.create = function(app, opts) {
return new Server(app, opts);
};
var Server = function (app, opts) {
this.opts = opts || {};
this.app = app;
this.globalFilterService = null;
this.filterService = null;
this.handlerService = null;
this.crons = [];
this.jobs = {};
this.state = ST_INITED;
app.event.on(events.ADD_CRONS, this.addCrons.bind(this));
app.event.on(events.REMOVE_CRONS, this.removeCrons.bind(this));
};
var pro = Server.prototype;
/**
* Server lifecycle callback
*/
pro.start = function() {
if(this.state > ST_INITED) {
return;
}
this.globalFilterService = initFilter(true, this.app);
this.filterService = initFilter(false, this.app);
this.handlerService = initHandler(this.app, this.opts);
this.cronHandlers = loadCronHandlers(this.app);
loadCrons(this, this.app);
this.state = ST_STARTED;
};
pro.afterStart = function() {
scheduleCrons(this, this.crons);
};
/**
* Stop server
*/
pro.stop = function() {
this.state = ST_STOPED;
};
/**
* Global handler.
*
* @param {Object} msg request message
* @param {Object} session session object
* @param {Callback} callback function
*/
pro.globalHandle = function(msg, session, cb) {
if(this.state !== ST_STARTED) {
utils.invokeCallback(cb, new Error('server not started'));
return;
}
var routeRecord = parseRoute(msg.route);
if(!routeRecord) {
utils.invokeCallback(cb, new Error('meet unknown route message %j', msg.route));
return;
}
var self = this;
var dispatch = function(err, resp, opts) {
if(err) {
handleError(true, self, err, msg, session, resp, opts, function(err, resp, opts) {
response(true, self, err, msg, session, resp, opts, cb);
});
return;
}
if(self.app.getServerType() !== routeRecord.serverType) {
doForward(self.app, msg, session, routeRecord, function(err, resp, opts) {
response(true, self, err, msg, session, resp, opts, cb);
});
} else {
doHandle(self, msg, session, routeRecord, function(err, resp, opts) {
response(true, self, err, msg, session, resp, opts, cb);
});
}
};
beforeFilter(true, self, msg, session, dispatch);
};
/**
* Handle request
*/
pro.handle = function(msg, session, cb) {
if(this.state !== ST_STARTED) {
cb(new Error('server not started'));
return;
}
var routeRecord = parseRoute(msg.route);
doHandle(this, msg, session, routeRecord, cb);
};
/**
* Add crons at runtime.
*
* @param {Array} crons would be added in application
*/
pro.addCrons = function(crons) {
this.cronHandlers = loadCronHandlers(this.app);
for(var i=0, l=crons.length; i<l; i++) {
var cron = crons[i];
checkAndAdd(cron, this.crons, this);
}
scheduleCrons(this, crons);
};
/**
* Remove crons at runtime.
*
* @param {Array} crons would be removed in application
*/
pro.removeCrons = function(crons) {
for(var i=0, l=crons.length; i<l; i++) {
var cron = crons[i];
var id = parseInt(cron.id);
if(!!this.jobs[id]) {
schedule.cancelJob(this.jobs[id]);
} else {
logger.warn('cron is not in application: %j', cron);
}
}
};
var initFilter = function(isGlobal, app) {
var service = new FilterService();
var befores, afters;
if(isGlobal) {
befores = app.get(Constants.KEYWORDS.GLOBAL_BEFORE_FILTER);
afters = app.get(Constants.KEYWORDS.GLOBAL_AFTER_FILTER);
} else {
befores = app.get(Constants.KEYWORDS.BEFORE_FILTER);
afters = app.get(Constants.KEYWORDS.AFTER_FILTER);
}
var i, l;
if(befores) {
for(i=0, l=befores.length; i<l; i++) {
service.before(befores[i]);
}
}
if(afters) {
for(i=0, l=afters.length; i<l; i++) {
service.after(afters[i]);
}
}
return service;
};
var initHandler = function(app, opts) {
return new HandlerService(app, opts);
};
/**
* Load cron handlers from current application
*/
var loadCronHandlers = function(app) {
var p = pathUtil.getCronPath(app.getBase(), app.getServerType());
if(p) {
return Loader.load(p, app);
}
};
/**
* Load crons from configure file
*/
var loadCrons = function(server, app) {
var env = app.get(Constants.RESERVED.ENV);
var p = path.join(app.getBase(), Constants.FILEPATH.CRON);
if(!fs.existsSync(p)) {
p = path.join(app.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.CRON));
if (!fs.existsSync(p)) {
return;
}
}
app.loadConfigBaseApp(Constants.RESERVED.CRONS, Constants.FILEPATH.CRON);
var crons = app.get(Constants.RESERVED.CRONS);
for(var serverType in crons) {
if(app.serverType === serverType) {
var list = crons[serverType];
for(var i = 0; i<list.length; i++) {
if(!list[i].serverId) {
checkAndAdd(list[i], server.crons, server);
} else {
if(app.serverId === list[i].serverId) {
checkAndAdd(list[i], server.crons, server);
}
}
}
}
}
};
/**
* Fire before filter chain if any
*/
var beforeFilter = function(isGlobal, server, msg, session, cb) {
var fm;
if(isGlobal) {
fm = server.globalFilterService;
} else {
fm = server.filterService;
}
if(fm) {
fm.beforeFilter(msg, session, cb);
} else {
utils.invokeCallback(cb);
}
};
/**
* Fire after filter chain if have
*/
var afterFilter = function(isGlobal, server, err, msg, session, resp, opts, cb) {
var fm;
if(isGlobal) {
fm = server.globalFilterService;
} else {
fm = server.filterService;
}
if(fm) {
if(isGlobal) {
fm.afterFilter(err, msg, session, resp, function() {
// do nothing
});
} else {
fm.afterFilter(err, msg, session, resp, function(err) {
cb(err, resp, opts);
});
}
}
};
/**
* pass err to the global error handler if specified
*/
var handleError = function(isGlobal, server, err, msg, session, resp, opts, cb) {
var handler;
if(isGlobal) {
handler = server.app.get(Constants.RESERVED.GLOBAL_ERROR_HANDLER);
} else {
handler = server.app.get(Constants.RESERVED.ERROR_HANDLER);
}
if(!handler) {
logger.debug('no default error handler to resolve unknown exception. ' + err.stack);
utils.invokeCallback(cb, err, resp, opts);
} else {
if(handler.length === 5) {
handler(err, msg, resp, session, cb);
} else {
handler(err, msg, resp, session, opts, cb);
}
}
};
/**
* Send response to client and fire after filter chain if any.
*/
var response = function(isGlobal, server, err, msg, session, resp, opts, cb) {
if(isGlobal) {
cb(err, resp, opts);
// after filter should not interfere response
afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);
} else {
afterFilter(isGlobal, server, err, msg, session, resp, opts, cb);
}
};
/**
* Parse route string.
*
* @param {String} route route string, such as: serverName.handlerName.methodName
* @return {Object} parse result object or null for illeagle route string
*/
var parseRoute = function(route) {
if(!route) {
return null;
}
var ts = route.split('.');
if(ts.length !== 3) {
return null;
}
return {
route: route,
serverType: ts[0],
handler: ts[1],
method: ts[2]
};
};
var doForward = function(app, msg, session, routeRecord, cb) {
var finished = false;
//should route to other servers
try {
app.sysrpc[routeRecord.serverType].msgRemote.forwardMessage(
// app.sysrpc[routeRecord.serverType].msgRemote.forwardMessage2(
session,
msg,
// msg.oldRoute || msg.route,
// msg.body,
// msg.aesPassword,
// msg.compressGzip,
session.export(),
function(err, resp, opts) {
if(err) {
logger.error('fail to process remote message:' + err.stack);
}
finished = true;
utils.invokeCallback(cb, err, resp, opts);
}
);
} catch(err) {
if(!finished) {
logger.error('fail to forward message:' + err.stack);
utils.invokeCallback(cb, err);
}
}
};
var doHandle = function(server, msg, session, routeRecord, cb) {
var originMsg = msg;
msg = msg.body || {};
msg.__route__ = originMsg.route;
var self = server;
var handle = function(err, resp, opts) {
if(err) {
// error from before filter
handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {
response(false, self, err, msg, session, resp, opts, cb);
});
return;
}
self.handlerService.handle(routeRecord, msg, session, function(err, resp, opts) {
if(err) {
//error from handler
handleError(false, self, err, msg, session, resp, opts, function(err, resp, opts) {
response(false, self, err, msg, session, resp, opts, cb);
});
return;
}
response(false, self, err, msg, session, resp, opts, cb);
});
}; //end of handle
beforeFilter(false, server, msg, session, handle);
};
/**
* Schedule crons
*/
var scheduleCrons = function(server, crons) {
var handlers = server.cronHandlers;
for(var i = 0; i<crons.length; i++) {
var cronInfo = crons[i];
var time = cronInfo.time;
var action = cronInfo.action;
var jobId = cronInfo.id;
if(!time || !action || !jobId) {
logger.error('cron miss necessary parameters: %j', cronInfo);
continue;
}
if(action.indexOf('.') < 0) {
logger.error('cron action is error format: %j', cronInfo);
continue;
}
var cron = action.split('.')[0];
var job = action.split('.')[1];
var handler = handlers[cron];
if(!handler) {
logger.error('could not find cron: %j', cronInfo);
continue;
}
if(typeof handler[job] !== 'function') {
logger.error('could not find cron job: %j, %s', cronInfo, job);
continue;
}
var id = schedule.scheduleJob(time, handler[job].bind(handler));
server.jobs[jobId] = id;
}
};
/**
* If cron is not in crons then put it in the array.
*/
var checkAndAdd = function(cron, crons, server) {
if(!containCron(cron.id, crons)) {
server.crons.push(cron);
} else {
logger.warn('cron is duplicated: %j', cron);
}
};
/**
* Check if cron is in crons.
*/
var containCron = function(id, crons) {
for(var i=0, l=crons.length; i<l; i++) {
if(id === crons[i].id) {
return true;
}
}
return false;
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| appUtil.js | 29.71% | (41 / 138) | 9.38% | (6 / 64) | 40% | (6 / 15) | 29.71% | (41 / 138) | |
| constants.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| countDownLatch.js | 16% | (4 / 25) | 0% | (0 / 18) | 0% | (0 / 4) | 16% | (4 / 25) | |
| events.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| log.js | 40% | (2 / 5) | 100% | (0 / 0) | 0% | (0 / 1) | 40% | (2 / 5) | |
| moduleUtil.js | 21.82% | (12 / 55) | 0% | (0 / 28) | 0% | (0 / 5) | 21.82% | (12 / 55) | |
| pathUtil.js | 41.38% | (12 / 29) | 0% | (0 / 10) | 0% | (0 / 9) | 41.38% | (12 / 29) | |
| utils.js | 16.16% | (32 / 198) | 0.9% | (1 / 111) | 8.7% | (2 / 23) | 16.33% | (32 / 196) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 1 1 1 2 2 2 2 2 1 2 2 1 2 2 1 2 1 1 2 2 2 2 2 2 1 | var async = require('async');
var log = require('./log');
var utils = require('./utils');
var path = require('path');
var fs = require('fs');
var Constants = require('./constants');
var starter = require('../master/starter');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
/**
* Initialize application configuration.
*/
module.exports.defaultConfiguration = function(app) {
var args = parseArgs(process.argv);
setupEnv(app, args);
loadMaster(app);
loadServers(app);
processArgs(app, args);
configLogger(app);
loadLifecycle(app);
};
/**
* Start servers by type.
*/
module.exports.startByType = function(app, cb) {
if(!!app.startId) {
if(app.startId === Constants.RESERVED.MASTER) {
utils.invokeCallback(cb);
} else {
starter.runServers(app);
}
} else {
if(!!app.type && app.type !== Constants.RESERVED.ALL && app.type !== Constants.RESERVED.MASTER) {
starter.runServers(app);
} else {
utils.invokeCallback(cb);
}
}
};
/**
* Load default components for application.
*/
module.exports.loadDefaultComponents = function(app) {
var pomelo = require('../pomelo');
// load system default components
if (app.serverType === Constants.RESERVED.MASTER) {
app.load(pomelo.master, app.get('masterConfig'));
} else {
app.load(pomelo.proxy, app.get('proxyConfig'));
if (app.getCurServer().port) {
app.load(pomelo.remote, app.get('remoteConfig'));
}
if (app.isFrontend()) {
app.load(pomelo.connection, app.get('connectionConfig'));
app.load(pomelo.connector, app.get('connectorConfig'));
app.load(pomelo.session, app.get('sessionConfig'));
// compatible for schedulerConfig
if(app.get('schedulerConfig')) {
app.load(pomelo.pushScheduler, app.get('schedulerConfig'));
} else {
app.load(pomelo.pushScheduler, app.get('pushSchedulerConfig'));
}
}
app.load(pomelo.backendSession, app.get('backendSessionConfig'));
app.load(pomelo.channel, app.get('channelConfig'));
app.load(pomelo.server, app.get('serverConfig'));
}
app.load(pomelo.monitor, app.get('monitorConfig'));
};
/**
* Stop components.
*
* @param {Array} comps component list
* @param {Number} index current component index
* @param {Boolean} force whether stop component immediately
* @param {Function} cb
*/
module.exports.stopComps = function(comps, index, force, cb) {
if (index >= comps.length) {
utils.invokeCallback(cb);
return;
}
var comp = comps[index];
if (typeof comp.stop === 'function') {
comp.stop(force, function() {
// ignore any error
module.exports.stopComps(comps, index + 1, force, cb);
});
} else {
module.exports.stopComps(comps, index + 1, force, cb);
}
};
/**
* Apply command to loaded components.
* This method would invoke the component {method} in series.
* Any component {method} return err, it would return err directly.
*
* @param {Array} comps loaded component list
* @param {String} method component lifecycle method name, such as: start, stop
* @param {Function} cb
*/
module.exports.optComponents = function(comps, method, cb) {
var i = 0;
async.forEachSeries(comps, function(comp, done) {
i++;
if (typeof comp[method] === 'function') {
comp[method](done);
} else {
done();
}
}, function(err) {
if (err) {
if(typeof err === 'string') {
logger.error('fail to operate component, method: %s, err: %j', method, err);
} else {
logger.error('fail to operate component, method: %s, err: %j', method, err.stack);
}
}
utils.invokeCallback(cb, err);
});
};
/**
* Load server info from config/servers.json.
*/
var loadServers = function(app) {
app.loadConfigBaseApp(Constants.RESERVED.SERVERS, Constants.FILEPATH.SERVER);
var servers = app.get(Constants.RESERVED.SERVERS);
var serverMap = {}, slist, i, l, server;
for (var serverType in servers) {
slist = servers[serverType];
for (i = 0, l = slist.length; i < l; i++) {
server = slist[i];
server.serverType = serverType;
if(server[Constants.RESERVED.CLUSTER_COUNT]) {
utils.loadCluster(app, server, serverMap);
continue;
}
serverMap[server.id] = server;
if (server.wsPort) {
logger.warn('wsPort is deprecated, use clientPort in frontend server instead, server: %j', server);
}
}
}
app.set(Constants.KEYWORDS.SERVER_MAP, serverMap);
};
/**
* Load master info from config/master.json.
*/
var loadMaster = function(app) {
app.loadConfigBaseApp(Constants.RESERVED.MASTER, Constants.FILEPATH.MASTER);
app.master = app.get(Constants.RESERVED.MASTER);
};
/**
* Process server start command
*/
var processArgs = function(app, args) {
var serverType = args.serverType || Constants.RESERVED.MASTER;
var serverId = args.id || app.getMaster().id;
var mode = args.mode || Constants.RESERVED.CLUSTER;
var masterha = args.masterha || 'false';
var type = args.type || Constants.RESERVED.ALL;
var startId = args.startId;
app.set(Constants.RESERVED.MAIN, args.main, true);
app.set(Constants.RESERVED.SERVER_TYPE, serverType, true);
app.set(Constants.RESERVED.SERVER_ID, serverId, true);
app.set(Constants.RESERVED.MODE, mode, true);
app.set(Constants.RESERVED.TYPE, type, true);
if(!!startId) {
app.set(Constants.RESERVED.STARTID, startId, true);
}
if (masterha === 'true') {
app.master = args;
app.set(Constants.RESERVED.CURRENT_SERVER, args, true);
} else if (serverType !== Constants.RESERVED.MASTER) {
app.set(Constants.RESERVED.CURRENT_SERVER, args, true);
} else {
app.set(Constants.RESERVED.CURRENT_SERVER, app.getMaster(), true);
}
};
/**
* Setup enviroment.
*/
var setupEnv = function(app, args) {
app.set(Constants.RESERVED.ENV, args.env || process.env.NODE_ENV || Constants.RESERVED.ENV_DEV, true);
};
/**
* Configure custom logger.
*/
var configLogger = function(app) {
if (process.env.POMELO_LOGGER !== 'off') {
var env = app.get(Constants.RESERVED.ENV);
var originPath = path.join(app.getBase(), Constants.FILEPATH.LOG);
var presentPath = path.join(app.getBase(), Constants.FILEPATH.CONFIG_DIR, env, path.basename(Constants.FILEPATH.LOG));
if(fs.existsSync(originPath)) {
log.configure(app, originPath);
} else if(fs.existsSync(presentPath)) {
log.configure(app, presentPath);
} else {
logger.error('logger file path configuration is error.');
}
}
};
/**
* Parse command line arguments.
*
* @param args command line arguments
*
* @return Object argsMap map of arguments
*/
var parseArgs = function(args) {
var argsMap = {};
var mainPos = 1;
while (args[mainPos].indexOf('--') > 0) {
mainPos++;
}
argsMap.main = args[mainPos];
for (var i = (mainPos + 1); i < args.length; i++) {
var arg = args[i];
var sep = arg.indexOf('=');
var key = arg.slice(0, sep);
var value = arg.slice(sep + 1);
if (!isNaN(Number(value)) && (value.indexOf('.') < 0)) {
value = Number(value);
}
argsMap[key] = value;
}
return argsMap;
};
/**
* Load lifecycle file.
*
*/
var loadLifecycle = function(app) {
var filePath = path.join(app.getBase(), Constants.FILEPATH.SERVER_DIR, app.serverType, Constants.FILEPATH.LIFECYCLE);
if(!fs.existsSync(filePath)) {
return;
}
var lifecycle = require(filePath);
for(var key in lifecycle) {
if(typeof lifecycle[key] === 'function') {
app.lifecycleCbs[key] = lifecycle[key];
} else {
logger.warn('lifecycle.js in %s is error format.', filePath);
}
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 1 | module.exports = {
KEYWORDS: {
BEFORE_FILTER: '__befores__',
AFTER_FILTER: '__afters__',
GLOBAL_BEFORE_FILTER: '__globalBefores__',
GLOBAL_AFTER_FILTER: '__globalAfters__',
ROUTE: '__routes__',
BEFORE_STOP_HOOK: '__beforeStopHook__',
MODULE: '__modules__',
SERVER_MAP: '__serverMap__',
RPC_BEFORE_FILTER: '__rpcBefores__',
RPC_AFTER_FILTER: '__rpcAfters__',
MASTER_WATCHER: '__masterwatcher__',
MONITOR_WATCHER: '__monitorwatcher__'
},
FILEPATH: {
MASTER: '/config/master.json',
SERVER: '/config/servers.json',
CRON: '/config/crons.json',
LOG: '/config/log4js.json',
SERVER_PROTOS: '/config/serverProtos.json',
CLIENT_PROTOS: '/config/clientProtos.json',
MASTER_HA: '/config/masterha.json',
LIFECYCLE: '/lifecycle.js',
SERVER_DIR: '/app/servers/',
CONFIG_DIR: '/config'
},
DIR: {
HANDLER: 'handler',
REMOTE: 'remote',
CRON: 'cron',
LOG: 'logs',
SCRIPT: 'scripts',
EVENT: 'events',
COMPONENT: 'components'
},
RESERVED: {
BASE: 'base',
MAIN: 'main',
MASTER: 'master',
SERVERS: 'servers',
ENV: 'env',
CPU: 'cpu',
ENV_DEV: 'development',
ENV_PRO: 'production',
ALL: 'all',
SERVER_TYPE: 'serverType',
SERVER_ID: 'serverId',
CURRENT_SERVER: 'curServer',
MODE: 'mode',
TYPE: 'type',
CLUSTER: 'clusters',
STAND_ALONE: 'stand-alone',
START: 'start',
AFTER_START: 'afterStart',
CRONS: 'crons',
ERROR_HANDLER: 'errorHandler',
GLOBAL_ERROR_HANDLER: 'globalErrorHandler',
AUTO_RESTART: 'auto-restart',
RESTART_FORCE: 'restart-force',
CLUSTER_COUNT: 'clusterCount',
CLUSTER_PREFIX: 'cluster-server-',
CLUSTER_SIGNAL: '++',
RPC_ERROR_HANDLER: 'rpcErrorHandler',
SERVER: 'server',
CLIENT: 'client',
STARTID: 'startId',
STOP_SERVERS: 'stop_servers',
SSH_CONFIG_PARAMS: 'ssh_config_params'
},
COMMAND: {
TASKSET: 'taskset',
KILL: 'kill',
TASKKILL: 'taskkill',
SSH: 'ssh'
},
PLATFORM: {
WIN: 'win32',
LINUX: 'linux'
},
LIFECYCLE: {
BEFORE_STARTUP: 'beforeStartup',
BEFORE_SHUTDOWN: 'beforeShutdown',
AFTER_STARTUP: 'afterStartup',
AFTER_STARTALL: 'afterStartAll'
},
SIGNAL: {
FAIL: 0,
OK: 1
},
TIME: {
TIME_WAIT_STOP: 3 * 1000,
TIME_WAIT_KILL: 5 * 1000,
TIME_WAIT_RESTART: 5 * 1000,
TIME_WAIT_COUNTDOWN: 10 * 1000,
TIME_WAIT_MASTER_KILL: 2 * 60 * 1000,
TIME_WAIT_MONITOR_KILL: 2 * 1000,
TIME_WAIT_PING: 30 * 1000,
TIME_WAIT_MAX_PING: 5 * 60 * 1000,
DEFAULT_UDP_HEARTBEAT_TIME: 20 * 1000,
DEFAULT_UDP_HEARTBEAT_TIMEOUT: 100 * 1000,
DEFAULT_MQTT_HEARTBEAT_TIMEOUT: 90 * 1000
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 1 1 1 1 | var exp = module.exports; /** * Count down to zero or timeout and invoke cb finally. */ var CountDownLatch = function(count, opts, cb) { this.count = count; this.cb = cb; var self = this; if (opts.timeout) { this.timerId = setTimeout(function() { self.cb(true); }, opts.timeout); } }; /** * Call when a task finish to count down. * * @api public */ CountDownLatch.prototype.done = function() { if(this.count <= 0) { throw new Error('illegal state.'); } this.count--; if (this.count === 0) { if (this.timerId) { clearTimeout(this.timerId); } this.cb(); } }; /** * Create a count down latch * * @param {Integer} count * @param {Object} opts, opts.timeout indicates timeout, optional param * @param {Function} cb, cb(isTimeout) * * @api public */ exp.createCountDownLatch = function(count, opts, cb) { if(!count || count <= 0) { throw new Error('count should be positive.'); } if (!cb && typeof opts === 'function') { cb = opts; opts = {}; } if(typeof cb !== 'function') { throw new Error('cb should be a function.'); } return new CountDownLatch(count, opts, cb); }; |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 1 | module.exports = {
ADD_SERVERS: 'add_servers',
REMOVE_SERVERS: 'remove_servers',
REPLACE_SERVERS: 'replace_servers',
BIND_SESSION: 'bind_session',
UNBIND_SESSION:'unbind_session',
CLOSE_SESSION: 'close_session',
ADD_CRONS: 'add_crons',
REMOVE_CRONS: 'remove_crons',
START_SERVER: 'start_server',
START_ALL: 'start_all'
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 | 1 1 | var logger = require('pomelo-logger');
/**
* Configure pomelo logger
*/
module.exports.configure = function(app, filename) {
var serverId = app.getServerId();
var base = app.getBase();
logger.configure(filename, {serverId: serverId, base: base});
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 1 1 1 1 1 1 1 1 1 1 1 1 | var os = require('os');
var admin = require('pomelo-admin');
var utils = require('./utils');
var Constants = require('./constants');
var pathUtil = require('./pathUtil');
var starter = require('../master/starter');
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var pro = module.exports;
/**
* Load admin modules
*/
pro.loadModules = function(self, consoleService) {
// load app register modules
var _modules = self.app.get(Constants.KEYWORDS.MODULE);
if(!_modules) {
return;
}
var modules = [];
for(var m in _modules){
modules.push(_modules[m]);
}
var record, moduleId, module;
for(var i=0, l=modules.length; i<l; i++) {
record = modules[i];
if(typeof record.module === 'function') {
module = record.module(record.opts, consoleService);
} else {
module = record.module;
}
moduleId = record.moduleId || module.moduleId;
if(!moduleId) {
logger.warn('ignore an unknown module.');
continue;
}
consoleService.register(moduleId, module);
self.modules.push(module);
}
};
pro.startModules = function(modules, cb) {
// invoke the start lifecycle method of modules
if(!modules) {
return;
}
startModule(null, modules, 0, cb);
};
/**
* Append the default system admin modules
*/
pro.registerDefaultModules = function(isMaster, app, closeWatcher) {
if(!closeWatcher) {
if(isMaster) {
app.registerAdmin(require('../modules/masterwatcher'), {app: app});
} else {
app.registerAdmin(require('../modules/monitorwatcher'), {app: app});
}
}
app.registerAdmin(admin.modules.watchServer,{app:app});
app.registerAdmin(require('../modules/console'), {app: app, starter: starter});
if(app.enabled('systemMonitor')) {
if(os.platform() !== Constants.PLATFORM.WIN) {
app.registerAdmin(admin.modules.systemInfo);
app.registerAdmin(admin.modules.nodeInfo);
}
app.registerAdmin(admin.modules.monitorLog, {path: pathUtil.getLogPath(app.getBase())});
app.registerAdmin(admin.modules.scripts, {app:app, path: pathUtil.getScriptPath(app.getBase())});
if(os.platform() !== Constants.PLATFORM.WIN) {
app.registerAdmin(admin.modules.profiler);
}
}
};
var startModule = function(err, modules, index, cb) {
if(err || index >= modules.length) {
utils.invokeCallback(cb, err);
return;
}
var module = modules[index];
if(module && typeof module.start === 'function') {
module.start(function(err) {
startModule(err, modules, index + 1, cb);
});
} else {
startModule(err, modules, index + 1, cb);
}
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 1 1 1 1 1 1 1 1 1 1 1 1 | var fs = require('fs');
var path = require('path');
var Constants = require('./constants');
var exp = module.exports;
/**
* Get system remote service path
*
* @param {String} role server role: frontend, backend
* @return {String} path string if the path exist else null
*/
exp.getSysRemotePath = function(role) {
var p = path.join(__dirname, '/../common/remote/', role);
return fs.existsSync(p) ? p : null;
};
/**
* Get user remote service path
*
* @param {String} appBase application base path
* @param {String} serverType server type
* @return {String} path string if the path exist else null
*/
exp.getUserRemotePath = function(appBase, serverType) {
var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.REMOTE);
return fs.existsSync(p) ? p : null;
};
/**
* Get user remote cron path
*
* @param {String} appBase application base path
* @param {String} serverType server type
* @return {String} path string if the path exist else null
*/
exp.getCronPath = function(appBase, serverType) {
var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.CRON);
return fs.existsSync(p) ? p : null;
};
/**
* List all the subdirectory names of user remote directory
* which hold the codes for all the server types.
*
* @param {String} appBase application base path
* @return {Array} all the subdiretory name under servers/
*/
exp.listUserRemoteDir = function(appBase) {
var base = path.join(appBase, '/app/servers/');
var files = fs.readdirSync(base);
return files.filter(function(fn) {
if(fn.charAt(0) === '.') {
return false;
}
return fs.statSync(path.join(base, fn)).isDirectory();
});
};
/**
* Compose remote path record
*
* @param {String} namespace remote path namespace, such as: 'sys', 'user'
* @param {String} serverType
* @param {String} path remote service source path
* @return {Object} remote path record
*/
exp.remotePathRecord = function(namespace, serverType, path) {
return {namespace: namespace, serverType: serverType, path: path};
};
/**
* Get handler path
*
* @param {String} appBase application base path
* @param {String} serverType server type
* @return {String} path string if the path exist else null
*/
exp.getHandlerPath = function(appBase, serverType) {
var p = path.join(appBase, '/app/servers/', serverType, Constants.DIR.HANDLER);
return fs.existsSync(p) ? p : null;
};
/**
* Get admin script root path.
*
* @param {String} appBase application base path
* @return {String} script path string
*/
exp.getScriptPath = function(appBase) {
return path.join(appBase, Constants.DIR.SCRIPT);
};
/**
* Get logs path.
*
* @param {String} appBase application base path
* @return {String} logs path string
*/
exp.getLogPath = function(appBase) {
return path.join(appBase, Constants.DIR.LOG);
};
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 1 1 | var os = require('os');
var util = require('util');
var exec = require('child_process').exec;
var logger = require('pomelo-logger').getLogger('pomelo', __filename);
var Constants = require('./constants');
var pomelo = require('../pomelo');
var utils = module.exports;
/**
* Invoke callback with check
*/
utils.invokeCallback = function(cb) {
if (typeof cb === 'function') {
var len = arguments.length;
if(len == 1) {
return cb();
}
if(len == 2) {
return cb(arguments[1]);
}
if(len == 3) {
return cb(arguments[1], arguments[2]);
}
if(len == 4) {
return cb(arguments[1], arguments[2], arguments[3]);
}
var args = Array(len - 1);
for (i = 1; i < len; i++)
args[i - 1] = arguments[i];
cb.apply(null, args);
// cb.apply(null, Array.prototype.slice.call(arguments, 1));
}
};
/**
* Get the count of elements of object
*/
utils.size = function(obj) {
var count = 0;
for (var i in obj) {
if (obj.hasOwnProperty(i) && typeof obj[i] !== 'function') {
count++;
}
}
return count;
};
/**
* Check a string whether ends with another string
*/
utils.endsWith = function(str, suffix) {
if (typeof str !== 'string' || typeof suffix !== 'string' ||
suffix.length > str.length) {
return false;
}
return str.indexOf(suffix, str.length - suffix.length) !== -1;
};
/**
* Check a string whether starts with another string
*/
utils.startsWith = function(str, prefix) {
if (typeof str !== 'string' || typeof prefix !== 'string' ||
prefix.length > str.length) {
return false;
}
return str.indexOf(prefix) === 0;
};
/**
* Compare the two arrays and return the difference.
*/
utils.arrayDiff = function(array1, array2) {
var o = {};
for(var i = 0, len = array2.length; i < len; i++) {
o[array2[i]] = true;
}
var result = [];
for(i = 0, len = array1.length; i < len; i++) {
var v = array1[i];
if(o[v]) continue;
result.push(v);
}
return result;
};
/*
* Date format
*/
utils.format = function(date, format) {
format = format || 'MMddhhmm';
var o = {
"M+": date.getMonth() + 1, //month
"d+": date.getDate(), //day
"h+": date.getHours(), //hour
"m+": date.getMinutes(), //minute
"s+": date.getSeconds(), //second
"q+": Math.floor((date.getMonth() + 3) / 3), //quarter
"S": date.getMilliseconds() //millisecond
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] :
("00" + o[k]).substr(("" + o[k]).length));
}
}
return format;
};
/**
* check if has Chinese characters.
*/
utils.hasChineseChar = function(str) {
if (/.*[\u4e00-\u9fa5]+.*$/.test(str)) {
return true;
} else {
return false;
}
};
/**
* transform unicode to utf8
*/
utils.unicodeToUtf8 = function(str) {
var i, len, ch;
var utf8Str = "";
len = str.length;
for (i = 0; i < len; i++) {
ch = str.charCodeAt(i);
if ((ch >= 0x0) && (ch <= 0x7F)) {
utf8Str += str.charAt(i);
} else if ((ch >= 0x80) && (ch <= 0x7FF)) {
utf8Str += String.fromCharCode(0xc0 | ((ch >> 6) & 0x1F));
utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));
} else if ((ch >= 0x800) && (ch <= 0xFFFF)) {
utf8Str += String.fromCharCode(0xe0 | ((ch >> 12) & 0xF));
utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));
utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));
} else if ((ch >= 0x10000) && (ch <= 0x1FFFFF)) {
utf8Str += String.fromCharCode(0xF0 | ((ch >> 18) & 0x7));
utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));
utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));
} else if ((ch >= 0x200000) && (ch <= 0x3FFFFFF)) {
utf8Str += String.fromCharCode(0xF8 | ((ch >> 24) & 0x3));
utf8Str += String.fromCharCode(0x80 | ((ch >> 18) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));
utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));
} else if ((ch >= 0x4000000) && (ch <= 0x7FFFFFFF)) {
utf8Str += String.fromCharCode(0xFC | ((ch >> 30) & 0x1));
utf8Str += String.fromCharCode(0x80 | ((ch >> 24) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 18) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 12) & 0x3F));
utf8Str += String.fromCharCode(0x80 | ((ch >> 6) & 0x3F));
utf8Str += String.fromCharCode(0x80 | (ch & 0x3F));
}
}
return utf8Str;
};
/**
* Ping server to check if network is available
*
*/
utils.ping = function(host, cb) {
if(!module.exports.isLocal(host)) {
var cmd = 'ping -w 15 ' + host;
exec(cmd, function(err, stdout, stderr) {
if(!!err) {
cb(false);
return;
}
cb(true);
});
} else {
cb(true);
}
};
/**
* Check if server is exsit.
*
*/
utils.checkPort = function(server, cb) {
if (!server.port && !server.clientPort) {
this.invokeCallback(cb, 'leisure');
return;
}
var self = this;
var port = server.port || server.clientPort;
var host = server.host;
var generateCommand = function(self, host, port) {
var cmd;
var ssh_params = pomelo.app.get(Constants.RESERVED.SSH_CONFIG_PARAMS);
if(!!ssh_params && Array.isArray(ssh_params)) {
ssh_params = ssh_params.join(' ');
}
else {
ssh_params = "";
}
if (!self.isLocal(host)) {
cmd = util.format('ssh %s %s "netstat -an|awk \'{print $4}\'|grep %s|wc -l"', host, ssh_params, port);
} else {
cmd = util.format('netstat -an|awk \'{print $4}\'|grep %s|wc -l', port);
}
return cmd;
};
var cmd1 = generateCommand(self, host, port);
var child = exec(cmd1, function(err, stdout, stderr) {
if(err) {
logger.error('command %s execute with error: %j', cmd1, err.stack);
self.invokeCallback(cb, 'error');
} else if(stdout.trim() !== '0') {
self.invokeCallback(cb, 'busy');
} else {
port = server.clientPort;
var cmd2 = generateCommand(self, host, port);
exec(cmd2, function(err, stdout, stderr) {
if(err) {
logger.error('command %s execute with error: %j', cmd2, err.stack);
self.invokeCallback(cb, 'error');
} else if (stdout.trim() !== '0') {
self.invokeCallback(cb, 'busy');
} else {
self.invokeCallback(cb, 'leisure');
}
});
}
});
};
utils.isLocal = function(host) {
var app = require('../pomelo').app;
if(!app) {
return host === '127.0.0.1' || host === 'localhost' || host === '0.0.0.0' || inLocal(host);
} else {
return host === '127.0.0.1' || host === 'localhost' || host === '0.0.0.0' || inLocal(host) || host === app.master.host;
}
};
/**
* Load cluster server.
*
*/
utils.loadCluster = function(app, server, serverMap) {
var increaseFields = {};
var host = server.host;
var count = parseInt(server[Constants.RESERVED.CLUSTER_COUNT]);
var seq = app.clusterSeq[server.serverType];
if(!seq) {
seq = 0;
app.clusterSeq[server.serverType] = count;
} else {
app.clusterSeq[server.serverType] = seq + count;
}
for(var key in server) {
var value = server[key].toString();
if(value.indexOf(Constants.RESERVED.CLUSTER_SIGNAL) > 0) {
var base = server[key].slice(0, -2);
increaseFields[key] = base;
}
}
var clone = function(src) {
var rs = {};
for(var key in src) {
rs[key] = src[key];
}
return rs;
};
for(var i=0, l=seq; i<count; i++,l++) {
var cserver = clone(server);
cserver.id = Constants.RESERVED.CLUSTER_PREFIX + server.serverType + '-' + l;
for(var k in increaseFields) {
var v = parseInt(increaseFields[k]);
cserver[k] = v + i;
}
serverMap[cserver.id] = cserver;
}
};
utils.extends = function(origin, add) {
if (!add || !this.isObject(add)) return origin;
var keys = Object.keys(add);
var i = keys.length;
while (i--) {
origin[keys[i]] = add[keys[i]];
}
return origin;
};
utils.headHandler = function(headBuffer) {
var len = 0;
for(var i=1; i<4; i++) {
if(i > 1) {
len <<= 8;
}
len += headBuffer.readUInt8(i);
}
return len;
};
var inLocal = function(host) {
for (var index in localIps) {
if (host === localIps[index]) {
return true;
}
}
return false;
};
var localIps = function() {
var ifaces = os.networkInterfaces();
var ips = [];
var func = function(details) {
Eif (details.family === 'IPv4') {
ips.push(details.address);
}
};
for (var dev in ifaces) {
ifaces[dev].forEach(func);
}
return ips;
}();
utils.isObject = function(arg) {
return typeof arg === 'object' && arg !== null;
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| app.js | 25% | (2 / 8) | 100% | (0 / 0) | 0% | (0 / 2) | 25% | (2 / 8) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 2 2 | var pomelo = require('pomelo');
/**
* Init app for client.
*/
var app = pomelo.createApp();
app.set('name', '$');
// app configuration
app.configure('production|development', 'connector', function(){
app.set('connectorConfig',
{
connector : pomelo.connectors.hybridconnector,
heartbeat : 3,
useDict : true,
useProtobuf : true
});
});
// start app
app.start();
process.on('uncaughtException', function (err) {
console.error(' Caught exception: ' + err.stack);
});
|